[go: nahoru, domu]

Merge "Add slot table size validation" into androidx-main
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index dd1dd37..3fa2a9a 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -141,8 +141,8 @@
     field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult!> CREATOR;
   }
 
-  public interface ActivityResultCallback<O> {
-    method public void onActivityResult(O!);
+  public fun interface ActivityResultCallback<O> {
+    method public void onActivityResult(O result);
   }
 
   public interface ActivityResultCaller {
diff --git a/activity/activity/api/public_plus_experimental_current.txt b/activity/activity/api/public_plus_experimental_current.txt
index dd1dd37..3fa2a9a 100644
--- a/activity/activity/api/public_plus_experimental_current.txt
+++ b/activity/activity/api/public_plus_experimental_current.txt
@@ -141,8 +141,8 @@
     field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult!> CREATOR;
   }
 
-  public interface ActivityResultCallback<O> {
-    method public void onActivityResult(O!);
+  public fun interface ActivityResultCallback<O> {
+    method public void onActivityResult(O result);
   }
 
   public interface ActivityResultCaller {
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index 64662e2..39310a7a 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -140,8 +140,8 @@
     field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult!> CREATOR;
   }
 
-  public interface ActivityResultCallback<O> {
-    method public void onActivityResult(O!);
+  public fun interface ActivityResultCallback<O> {
+    method public void onActivityResult(O result);
   }
 
   public interface ActivityResultCaller {
diff --git a/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt b/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
index dad575f..a6af035 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
@@ -59,12 +59,10 @@
 
         // register for the result
         val activityResult = registry.register(
-            "test", lifecycleOwner,
-            TakePicturePreview(),
-            ActivityResultCallback {
-                resultReturned = true
-            }
-        )
+            "test", lifecycleOwner, TakePicturePreview()
+        ) {
+            resultReturned = true
+        }
 
         // move the state to started
         lifecycleOwner.currentState = Lifecycle.State.STARTED
@@ -82,8 +80,8 @@
         // register for the result
         val activityResult = registry.register(
             "test", lifecycleOwner,
-            TakePicturePreview(), ActivityResultCallback {}
-        )
+            TakePicturePreview()
+        ) {}
 
         // saved the state of the registry
         val state = Bundle()
@@ -101,11 +99,10 @@
         var resultReturned = false
         // re-register for the result that should have been saved
         registry.register(
-            "test", lifecycleOwner, TakePicturePreview(),
-            ActivityResultCallback {
-                resultReturned = true
-            }
-        )
+            "test", lifecycleOwner, TakePicturePreview()
+        ) {
+            resultReturned = true
+        }
 
         lifecycleOwner.currentState = Lifecycle.State.STARTED
 
@@ -119,9 +116,8 @@
         try {
             // register for the result
             registry.register(
-                "test", lifecycleOwner,
-                TakePicturePreview(), ActivityResultCallback {}
-            )
+                "test", lifecycleOwner, TakePicturePreview()
+            ) {}
             fail("Registering for activity result after Lifecycle ON_CREATE should fail")
         } catch (e: IllegalStateException) {
             assertThat(e).hasMessageThat().contains(
@@ -138,9 +134,8 @@
 
         // register for the result
         val activityResult = registry.register(
-            "test", lifecycleOwner,
-            TakePicturePreview(), ActivityResultCallback {}
-        )
+            "test", lifecycleOwner, TakePicturePreview()
+        ) {}
 
         // saved the state of the registry
         val state = Bundle()
@@ -155,11 +150,10 @@
         var resultReturned = false
         // re-register for the result that should have been saved
         registry.register(
-            "test", lifecycleOwner, TakePicturePreview(),
-            ActivityResultCallback {
-                resultReturned = true
-            }
-        )
+            "test", lifecycleOwner, TakePicturePreview()
+        ) {
+            resultReturned = true
+        }
 
         // launch the result
         activityResult.launch(null)
@@ -200,10 +194,9 @@
         var resultReturned = false
         val activityResult = dispatchResultRegistry.register(
             "test", lifecycleOwner, TakePicture(),
-            ActivityResultCallback {
-                resultReturned = true
-            }
-        )
+        ) {
+            resultReturned = true
+        }
 
         // launch the result
         activityResult.launch(null)
@@ -244,10 +237,9 @@
         var resultReturned = false
         val activityResult = dispatchResultRegistry.register(
             "test", lifecycleOwner, TakePicturePreview(),
-            ActivityResultCallback {
-                resultReturned = true
-            }
-        )
+        ) {
+            resultReturned = true
+        }
 
         // launch the result
         activityResult.launch(null)
@@ -276,9 +268,8 @@
 
         // register for the result
         val activityResult = registry.register(
-            "test", lifecycleOwner,
-            TakePicturePreview(), ActivityResultCallback {}
-        )
+            "test", lifecycleOwner, TakePicturePreview()
+        ) {}
 
         // saved the state of the registry
         val state = Bundle()
@@ -296,11 +287,10 @@
         var resultReturned = false
         // re-register for the result that should have been saved
         registry.register(
-            "test", lifecycleOwner, TakePicturePreview(),
-            ActivityResultCallback {
-                resultReturned = true
-            }
-        )
+            "test", lifecycleOwner, TakePicturePreview()
+        ) {
+            resultReturned = true
+        }
 
         // move to CREATED and make sure the callback is not fired
         lifecycleOwner.currentState = Lifecycle.State.CREATED
diff --git a/activity/activity/src/main/java/androidx/activity/Cancellable.java b/activity/activity/src/main/java/androidx/activity/Cancellable.kt
similarity index 90%
rename from activity/activity/src/main/java/androidx/activity/Cancellable.java
rename to activity/activity/src/main/java/androidx/activity/Cancellable.kt
index a5cb90a..33af37c 100644
--- a/activity/activity/src/main/java/androidx/activity/Cancellable.java
+++ b/activity/activity/src/main/java/androidx/activity/Cancellable.kt
@@ -13,17 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package androidx.activity;
+package androidx.activity
 
 /**
  * Token representing a cancellable operation.
  */
-interface Cancellable {
-
+internal interface Cancellable {
     /**
      * Cancel the subscription. This call should be idempotent, making it safe to
      * call multiple times.
      */
-    void cancel();
-}
+    fun cancel()
+}
\ No newline at end of file
diff --git a/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.java b/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.kt
similarity index 66%
rename from activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.java
rename to activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.kt
index fde2497..048ef0e 100644
--- a/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.java
+++ b/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.kt
@@ -13,23 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.activity.result
 
-
-package androidx.activity.result;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
+import android.app.Activity
 
 /**
- * A type-safe callback to be called when an {@link Activity#onActivityResult activity result}
+ * A type-safe callback to be called when an [activity result][Activity.onActivityResult]
  * is available.
- *
- * @param <O> result type
  */
-public interface ActivityResultCallback<O> {
-
+fun interface ActivityResultCallback<O : Any> {
     /**
      * Called when result is available
      */
-    void onActivityResult(@SuppressLint("UnknownNullness") O result);
+    fun onActivityResult(result: O)
 }
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
new file mode 100644
index 0000000..73d01c3
--- /dev/null
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark
+
+import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
+import androidx.benchmark.perfetto.PerfettoHelper
+import androidx.benchmark.perfetto.PerfettoTrace
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import kotlin.test.assertFailsWith
+import kotlin.test.assertNotNull
+import kotlin.test.fail
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalPerfettoCaptureApi::class)
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+class PerfettoTraceTest {
+    @Test
+    fun record_basic() {
+        assumeTrue(PerfettoHelper.isAbiSupported())
+        var perfettoTrace: PerfettoTrace? = null
+        PerfettoTrace.record(
+            fileLabel = "testTrace",
+            traceCallback = { trace ->
+                perfettoTrace = trace
+            }
+        ) {
+            // noop
+        }
+        assertNotNull(perfettoTrace)
+        assert(perfettoTrace!!.path.matches(Regex(".*/testTrace_[0-9-]+.perfetto-trace"))) {
+            "$perfettoTrace didn't match!"
+        }
+    }
+    @Test
+    fun record_reentrant() {
+        assumeTrue(PerfettoHelper.isAbiSupported())
+        var perfettoTrace: PerfettoTrace? = null
+        PerfettoTrace.record(
+            fileLabel = "outer",
+            traceCallback = { trace ->
+                perfettoTrace = trace
+            }
+        ) {
+            // tracing while tracing should fail
+            assertFailsWith<IllegalStateException> {
+                PerfettoTrace.record(
+                    fileLabel = "inner",
+                    traceCallback = { _ ->
+                        fail("inner trace should not complete / record")
+                    }
+                ) {
+                    // noop
+                }
+            }
+        }
+        assertNotNull(perfettoTrace)
+    }
+}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 58a1723..198ab7a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -39,6 +39,17 @@
         }
     }
 
+    companion object {
+        val inUseLock = Object()
+
+        /**
+         * Prevents re-entrance of perfetto trace capture, as it doesn't handle this correctly
+         *
+         * (Single file output location, process cleanup, etc.)
+         */
+        var inUse = false
+    }
+
     @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     private fun start(
         appTagPackages: List<String>,
@@ -63,23 +74,6 @@
     }
 
     @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
-    private fun stop(benchmarkName: String, iteration: Int?): String {
-        val traceName: String
-        val reportKey: String
-        if (iteration != null) {
-            val iterString = iteration.toString().padStart(3, '0')
-            traceName = "${benchmarkName}_iter${iterString}_${dateToFileName()}.perfetto-trace"
-            reportKey = "perfetto_trace_$iterString"
-        } else {
-            traceName = "${benchmarkName}_${dateToFileName()}.perfetto-trace"
-            reportKey = "perfetto_trace"
-        }
-        return Outputs.writeFile(fileName = traceName, reportKey = reportKey) {
-            capture!!.stop(it.absolutePath)
-        }
-    }
-
-    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     private fun stop(traceLabel: String): String {
         return Outputs.writeFile(
             fileName = "${traceLabel}_${dateToFileName()}.perfetto-trace",
@@ -102,6 +96,17 @@
             return null
         }
 
+        synchronized(inUseLock) {
+            if (inUse) {
+                throw IllegalStateException(
+                    "Reentrant Perfetto Tracing is not supported." +
+                        " This means you cannot use more than one of" +
+                        " BenchmarkRule/MacrobenchmarkRule/PerfettoTraceRule/PerfettoTrace.record" +
+                        " together."
+                )
+            }
+            inUse = true
+        }
         // Prior to Android 11 (R), a shell property must be set to enable perfetto tracing, see
         // https://perfetto.dev/docs/quickstart/android-tracing#starting-the-tracing-services
         val propOverride = if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
@@ -122,6 +127,9 @@
             return path
         } finally {
             propOverride?.resetIfOverridden()
+            synchronized(inUseLock) {
+                inUse = false
+            }
         }
     }
 }
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index 4d149d2..3e74e58 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -375,11 +375,8 @@
 
         fun isAbiSupported(): Boolean {
             Log.d(LOG_TAG, "Supported ABIs: ${Build.SUPPORTED_ABIS.joinToString()}")
-            // Cuttlefish is x86 but claims support for x86_64
-            return !Build.MODEL.contains("Cuttlefish") && ( // b/204892353
-                Build.SUPPORTED_64_BIT_ABIS.any { SUPPORTED_64_ABIS.contains(it) } ||
-                    Build.SUPPORTED_32_BIT_ABIS.any { SUPPORTED_32_ABIS.contains(it) }
-                )
+            return Build.SUPPORTED_64_BIT_ABIS.any { SUPPORTED_64_ABIS.contains(it) } ||
+                Build.SUPPORTED_32_BIT_ABIS.any { SUPPORTED_32_ABIS.contains(it) }
         }
 
         @get:TestOnly
@@ -399,6 +396,8 @@
                     // supported by a device. That is why we need to search from most specific to
                     // least specific. For e.g. emulators claim to support aarch64, when in reality
                     // they can only support x86 or x86_64.
+                    // Note: Cuttlefish is x86 but claims support for x86_64
+                    Build.MODEL.contains("Cuttlefish") -> "x86" // TODO(204892353): handle properly
                     Build.SUPPORTED_64_BIT_ABIS.any { it.startsWith("x86_64") } -> "x86_64"
                     Build.SUPPORTED_32_BIT_ABIS.any { it.startsWith("x86") } -> "x86"
                     Build.SUPPORTED_64_BIT_ABIS.any { it.startsWith("arm64") } -> "aarch64"
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
index 33bdde3..98ed9d8 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
@@ -35,6 +35,15 @@
         /**
          * Record a Perfetto System Trace for the specified [block].
          *
+         * ```
+         * PerfettoTrace.record("myTrace") {
+         *     // content in here is traced to myTrace_<timestamp>.perfetto_trace
+         * }
+         * ```
+         *
+         * Reentrant Perfetto trace capture is not supported, so this API may not be combined with
+         * `BenchmarkRule`, `MacrobenchmarkRule`, or `PerfettoTraceRule`.
+         *
          * If the block throws, the trace is still captured and passed to [traceCallback].
          */
         @JvmStatic
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
index e98a49b..a016541 100644
--- a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
@@ -20,6 +20,8 @@
 import java.util.concurrent.atomic.AtomicBoolean
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
@@ -109,15 +111,47 @@
             it.xcResultPath.set(runDarwinBenchmarks.flatMap { task ->
                 task.xcResultPath
             })
+            val resultFileName = "${extension.xcodeProjectName.get()}-benchmark-result.json"
             it.outputFile.set(
                 project.layout.buildDirectory.file(
-                    "${extension.xcodeProjectName.get()}-benchmark-result.json"
+                    resultFileName
                 )
             )
+            val provider = project.benchmarksDistributionDirectory(extension)
+            val resultFile = provider.map { directory ->
+                directory.file(resultFileName)
+            }
+            it.distFile.set(resultFile)
+        }
+    }
+
+    private fun Project.benchmarksDistributionDirectory(
+        extension: DarwinBenchmarkPluginExtension
+    ): Provider<Directory> {
+        val distProvider = project.distributionDirectory()
+        val benchmarksDirProvider = distProvider.flatMap { distDir ->
+            extension.xcodeProjectName.map { projectName ->
+                val projectPath = project.path.replace(":", "/")
+                val benchmarksDirectory = File(distDir, DARWIN_BENCHMARKS_DIR)
+                File(benchmarksDirectory, "$projectPath/$projectName")
+            }
+        }
+        return project.layout.dir(benchmarksDirProvider)
+    }
+
+    private fun Project.distributionDirectory(): Provider<File> {
+        // We want to write metrics to library metrics specific location
+        // Context: b/257326666
+        return providers.environmentVariable(DIST_DIR).map { value ->
+            File(value, LIBRARY_METRICS)
         }
     }
 
     private companion object {
+        // Environment variables
+        const val DIST_DIR = "DIST_DIR"
+        const val LIBRARY_METRICS = "librarymetrics"
+        const val DARWIN_BENCHMARKS_DIR = "darwinBenchmarks"
         // Gradle Properties
         const val XCODEGEN_DOWNLOAD_URI = "androidx.benchmark.darwin.xcodeGenDownloadUri"
 
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
index b95352d..7d4136d 100644
--- a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
@@ -27,6 +27,7 @@
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
@@ -44,6 +45,10 @@
     @get:OutputFile
     abstract val outputFile: RegularFileProperty
 
+    @get:OutputFile
+    @get:Optional
+    abstract val distFile: RegularFileProperty
+
     @TaskAction
     fun benchmarkResults() {
         val xcResultFile = xcResultPath.get().asFile
@@ -68,5 +73,10 @@
             .toJson(metrics)
 
         outputFile.get().asFile.writeText(output)
+
+        // Add output to the DIST_DIR when specified
+        if (distFile.isPresent) {
+            distFile.get().asFile.writeText(output)
+        }
     }
 }
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
index a39994d..aa35558 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
@@ -29,6 +29,17 @@
 /**
  * Add this rule to record a Perfetto trace for each test on Android Lollipop (API 21)+ devices.
  *
+ * ```
+ * @RunWith(AndroidJUnit4::class)
+ * class PerfettoOverheadBenchmark {
+ *     // traces all tests in file
+ *     @get:Rule
+ *     val perfettoRule = PerfettoTraceRule()
+ *
+ *     @Test
+ *     fun test() {}
+ * }
+ * ```
  * Captured traces can be observed through any of:
  * * Android Studio trace linking under `Benchmark` in test output tab
  * * The optional `traceCallback` parameter
@@ -44,6 +55,9 @@
  * ```
  * > adb pull /storage/emulated/0/Android/data/mypackage.test/files/PerfettoCaptureTest.trace
  * ```
+ *
+ * Reentrant Perfetto trace capture is not supported, so this API may not be combined with
+ * `BenchmarkRule`, `MacrobenchmarkRule`, or `PerfettoTrace.record`.
  */
 @ExperimentalPerfettoCaptureApi
 class PerfettoTraceRule(
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
index 1db82176..de94880 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
@@ -17,6 +17,7 @@
 package androidx.benchmark.macro
 
 import androidx.annotation.RequiresApi
+import androidx.benchmark.DeviceInfo
 import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -29,12 +30,19 @@
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertTrue
+import org.junit.Assume.assumeFalse
+import org.junit.Before
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 @OptIn(ExperimentalMacrobenchmarkApi::class)
 class MacrobenchmarkTest {
 
+    @Before
+    fun setUp() {
+        assumeFalse(DeviceInfo.isEmulator)
+    }
+
     @Test
     fun macrobenchmarkWithStartupMode_emptyMetricList() {
         val exception = assertFailsWith<IllegalArgumentException> {
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
index b3561e8..4deb295 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
@@ -20,6 +20,7 @@
 import android.content.Intent
 import android.os.Build
 import androidx.annotation.RequiresApi
+import androidx.benchmark.DeviceInfo
 import androidx.benchmark.Outputs
 import androidx.benchmark.macro.perfetto.PerfettoTraceProcessor
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
@@ -37,6 +38,7 @@
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
+import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -182,12 +184,14 @@
     @LargeTest
     @Test
     fun startupInAppNav_immediate() {
+        assumeFalse(DeviceInfo.isEmulator) // TODO(b/255754739): address failures on Cuttlefish
         validateStartup_fullyDrawn(delayMs = 0, useInAppNav = true)
     }
 
     @LargeTest
     @Test
     fun startupInAppNav_fullyDrawn() {
+        assumeFalse(DeviceInfo.isEmulator) // TODO(b/255754739): address failures on Cuttlefish
         validateStartup_fullyDrawn(delayMs = 100, useInAppNav = true)
     }
 
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
index cc8012b..449e495 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
@@ -17,11 +17,10 @@
 package androidx.benchmark.benchmark
 
 import androidx.benchmark.junit4.BenchmarkRule
-import androidx.benchmark.junit4.PerfettoTraceRule
 import androidx.benchmark.junit4.measureRepeated
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.tracing.Trace
 import androidx.tracing.trace
 import org.junit.Rule
@@ -31,12 +30,11 @@
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 class PerfettoOverheadBenchmark {
-    @get:Rule
-    val benchmarkRule = BenchmarkRule()
+    private val targetPackage =
+        InstrumentationRegistry.getInstrumentation().targetContext.packageName
 
-    @OptIn(ExperimentalPerfettoCaptureApi::class)
     @get:Rule
-    val mPerfettoTraceRule = PerfettoTraceRule()
+    val benchmarkRule = BenchmarkRule(packages = listOf(targetPackage))
 
     /**
      * Empty baseline, no tracing. Expect similar results to [TrivialJavaBenchmark.nothing].
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
index 18de45a..0da127f 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
@@ -18,9 +18,7 @@
 
 import android.os.Build
 import androidx.benchmark.junit4.BenchmarkRule
-import androidx.benchmark.junit4.PerfettoTraceRule
 import androidx.benchmark.junit4.measureRepeated
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
 import androidx.benchmark.perfetto.PerfettoCapture
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -50,10 +48,6 @@
     @get:Rule
     val benchmarkRule = BenchmarkRule(packages = listOf(targetPackage))
 
-    @OptIn(ExperimentalPerfettoCaptureApi::class)
-    @get:Rule
-    val mPerfettoTraceRule = PerfettoTraceRule()
-
     private val testData = Array(50_000) { UUID.randomUUID().toString() }
 
     @Before
diff --git a/biometric/biometric/src/main/res/values-da/strings.xml b/biometric/biometric/src/main/res/values-da/strings.xml
index 41fe471..b08401b 100644
--- a/biometric/biometric/src/main/res/values-da/strings.xml
+++ b/biometric/biometric/src/main/res/values-da/strings.xml
@@ -17,11 +17,11 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="fingerprint_dialog_touch_sensor" msgid="1072308044213194243">"Sæt finger på fingeraftrykslæser"</string>
+    <string name="fingerprint_dialog_touch_sensor" msgid="1072308044213194243">"Sæt finger på fingeraftrykssensor"</string>
     <string name="fingerprint_not_recognized" msgid="3873359464293253009">"Ikke genkendt"</string>
     <string name="fingerprint_error_hw_not_available" msgid="8216738333501875566">"Hardwaren til fingeraftryk er ikke tilgængelig."</string>
     <string name="fingerprint_error_no_fingerprints" msgid="7520712796891883488">"Der er ikke registreret nogen fingeraftryk."</string>
-    <string name="fingerprint_error_hw_not_present" msgid="6306988885793029438">"Denne enhed har ingen fingeraftrykslæser"</string>
+    <string name="fingerprint_error_hw_not_present" msgid="6306988885793029438">"Denne enhed har ingen fingeraftrykssensor"</string>
     <string name="fingerprint_error_user_canceled" msgid="7627716295344353987">"Fingeraftrykshandlingen blev annulleret af brugeren."</string>
     <string name="fingerprint_error_lockout" msgid="7291787166416782245">"Der var for mange forsøg Prøv igen senere."</string>
     <string name="default_error_msg" msgid="4776854077120974966">"Ukendt fejl"</string>
diff --git a/browser/browser/api/current.txt b/browser/browser/api/current.txt
index f084924..0598270 100644
--- a/browser/browser/api/current.txt
+++ b/browser/browser/api/current.txt
@@ -73,7 +73,7 @@
     ctor public CustomTabsCallback();
     method public void extraCallback(String, android.os.Bundle?);
     method public android.os.Bundle? extraCallbackWithResult(String, android.os.Bundle?);
-    method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
+    method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, @Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
     method public void onMessageChannelReady(android.os.Bundle?);
     method public void onNavigationEvent(int, android.os.Bundle?);
     method public void onPostMessage(String, android.os.Bundle?);
@@ -118,7 +118,7 @@
     field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
     field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
     field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
-    field public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+    field public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
     field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
     field public static final String EXTRA_CLOSE_BUTTON_POSITION = "androidx.browser.customtabs.extra.CLOSE_BUTTON_POSITION";
     field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
diff --git a/browser/browser/api/public_plus_experimental_current.txt b/browser/browser/api/public_plus_experimental_current.txt
index f084924..0598270 100644
--- a/browser/browser/api/public_plus_experimental_current.txt
+++ b/browser/browser/api/public_plus_experimental_current.txt
@@ -73,7 +73,7 @@
     ctor public CustomTabsCallback();
     method public void extraCallback(String, android.os.Bundle?);
     method public android.os.Bundle? extraCallbackWithResult(String, android.os.Bundle?);
-    method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
+    method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, @Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
     method public void onMessageChannelReady(android.os.Bundle?);
     method public void onNavigationEvent(int, android.os.Bundle?);
     method public void onPostMessage(String, android.os.Bundle?);
@@ -118,7 +118,7 @@
     field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
     field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
     field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
-    field public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+    field public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
     field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
     field public static final String EXTRA_CLOSE_BUTTON_POSITION = "androidx.browser.customtabs.extra.CLOSE_BUTTON_POSITION";
     field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
diff --git a/browser/browser/api/restricted_current.txt b/browser/browser/api/restricted_current.txt
index 09759da..de4bb0c 100644
--- a/browser/browser/api/restricted_current.txt
+++ b/browser/browser/api/restricted_current.txt
@@ -84,7 +84,7 @@
     ctor public CustomTabsCallback();
     method public void extraCallback(String, android.os.Bundle?);
     method public android.os.Bundle? extraCallbackWithResult(String, android.os.Bundle?);
-    method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
+    method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, @Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
     method public void onMessageChannelReady(android.os.Bundle?);
     method public void onNavigationEvent(int, android.os.Bundle?);
     method public void onPostMessage(String, android.os.Bundle?);
@@ -129,7 +129,7 @@
     field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
     field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
     field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
-    field public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+    field public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
     field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
     field public static final String EXTRA_CLOSE_BUTTON_POSITION = "androidx.browser.customtabs.extra.CLOSE_BUTTON_POSITION";
     field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
diff --git a/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java b/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java
index 2caee24..6df7e8b 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java
@@ -45,7 +45,7 @@
 
     @Test
     public void testOnActivityResized() throws Throwable {
-        mToken.getCallback().onActivityResized(75239, new Bundle());
+        mToken.getCallback().onActivityResized(75239, 1200, new Bundle());
         assertTrue(mCallback.hasActivityBeenResized());
     }
 }
diff --git a/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java b/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java
index 3363e0f..9293212 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java
@@ -70,8 +70,8 @@
         }
 
         @Override
-        public void onActivityResized(int size, Bundle extras) throws RemoteException {
-            TestCustomTabsCallback.this.onActivityResized(size, extras);
+        public void onActivityResized(int height, int width, Bundle extras) throws RemoteException {
+            TestCustomTabsCallback.this.onActivityResized(height, width, extras);
         }
     };
 
@@ -104,7 +104,7 @@
     }
 
     @Override
-    public void onActivityResized(int size, Bundle extras) {
+    public void onActivityResized(int height, int width, @NonNull Bundle extras) {
         mOnResizedReceived = true;
     }
 
diff --git a/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl b/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl
index 38bb3e1..951e8ee 100644
--- a/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl
+++ b/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl
@@ -29,5 +29,5 @@
     void onPostMessage(String message, in Bundle extras) = 4;
     void onRelationshipValidationResult(int relation, in Uri origin, boolean result, in Bundle extras) = 5;
     Bundle extraCallbackWithResult(String callbackName, in Bundle args) = 6;
-    void onActivityResized(int size, in Bundle extras) = 7;
+    void onActivityResized(int height, int width, in Bundle extras) = 7;
 }
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
index 3ac0a3c..a111f89 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
@@ -161,12 +161,12 @@
             boolean result, @Nullable Bundle extras) {}
 
     /**
-     * Called when the tab is resized in its height. This is applicable when users resize a tab
-     * launched with {@link CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE} for the {@link
-     * CustomTabsIntent#ActivityResizeBehavior}.
+     * Called when the tab is resized.
      *
-     * @param size The updated height in px.
+     * @param height The updated height in px.
+     * @param width The updated width in px.
      * @param extras Reserved for future use.
      */
-    public void onActivityResized(@Dimension(unit = PX) int size, @NonNull Bundle extras) {}
+    public void onActivityResized(@Dimension(unit = PX) int height,
+            @Dimension(unit = PX) int width, @NonNull Bundle extras) {}
 }
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
index e45d218..36d1393 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
@@ -398,13 +398,14 @@
             }
 
             @Override
-            public void onActivityResized(final int size, final @Nullable Bundle extras)
+            public void onActivityResized(final int height, final int width,
+                    final @Nullable Bundle extras)
                     throws RemoteException {
                 if (callback == null) return;
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        callback.onActivityResized(size, extras);
+                        callback.onActivityResized(height, width, extras);
                     }
                 });
             }
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
index 9792aff..53c77a0 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
@@ -357,24 +357,24 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @IntDef({ACTIVITY_HEIGHT_DEFAULT, ACTIVITY_HEIGHT_ADJUSTABLE, ACTIVITY_HEIGHT_FIXED})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ActivityResizeBehavior {
+    public @interface ActivityHeightResizeBehavior {
     }
 
     /**
-     * Applies the default resize behavior for the Custom Tab Activity when it behaves as a
+     * Applies the default height resize behavior for the Custom Tab Activity when it behaves as a
      * bottom sheet.
      */
     public static final int ACTIVITY_HEIGHT_DEFAULT = 0;
 
     /**
-     * The Custom Tab Activity, when it behaves as a bottom sheet, can be manually resized by the
-     * user.
+     * The Custom Tab Activity, when it behaves as a bottom sheet, can have its height manually
+     * resized by the user.
      */
     public static final int ACTIVITY_HEIGHT_ADJUSTABLE = 1;
 
     /**
-     * The Custom Tab Activity, when it behaves as a bottom sheet, cannot be manually resized by
-     * the user.
+     * The Custom Tab Activity, when it behaves as a bottom sheet, cannot have its height manually
+     * resized by the user.
      */
     public static final int ACTIVITY_HEIGHT_FIXED = 2;
 
@@ -385,12 +385,12 @@
 
     /**
      * Extra that, if set in combination with
-     * {@link CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX}, defines the resize behavior of
-     * the Custom Tab Activity when it behaves as a bottom sheet.
+     * {@link CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX}, defines the height resize
+     * behavior of the Custom Tab Activity when it behaves as a bottom sheet.
      * Default is {@link CustomTabsIntent#ACTIVITY_HEIGHT_DEFAULT}.
      */
-    public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR =
-            "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+    public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR =
+            "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
 
     /**
      * Extra that sets the toolbar's top corner radii in dp. This will only have
@@ -980,27 +980,28 @@
          * The Custom Tab will behave as a bottom sheet.
          *
          * @param initialHeightPx The Custom Tab Activity's initial height in pixels.
-         * @param activityResizeBehavior Desired height behavior.
+         * @param activityHeightResizeBehavior Desired height behavior.
          * @see CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX
-         * @see CustomTabsIntent#EXTRA_ACTIVITY_RESIZE_BEHAVIOR
+         * @see CustomTabsIntent#EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR
          * @see CustomTabsIntent#ACTIVITY_HEIGHT_DEFAULT
          * @see CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE
          * @see CustomTabsIntent#ACTIVITY_HEIGHT_FIXED
          */
         @NonNull
         public Builder setInitialActivityHeightPx(@Dimension(unit = PX) int initialHeightPx,
-                @ActivityResizeBehavior int activityResizeBehavior) {
+                @ActivityHeightResizeBehavior int activityHeightResizeBehavior) {
             if (initialHeightPx <= 0) {
                 throw new IllegalArgumentException("Invalid value for the initialHeightPx "
                         + "argument");
             }
-            if (activityResizeBehavior < 0 || activityResizeBehavior > ACTIVITY_HEIGHT_MAX) {
-                throw new IllegalArgumentException("Invalid value for the activityResizeBehavior "
-                        + "argument");
+            if (activityHeightResizeBehavior < 0
+                    || activityHeightResizeBehavior > ACTIVITY_HEIGHT_MAX) {
+                throw new IllegalArgumentException(
+                        "Invalid value for the activityHeightResizeBehavior argument");
             }
 
             mIntent.putExtra(EXTRA_INITIAL_ACTIVITY_HEIGHT_PX, initialHeightPx);
-            mIntent.putExtra(EXTRA_ACTIVITY_RESIZE_BEHAVIOR, activityResizeBehavior);
+            mIntent.putExtra(EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR, activityHeightResizeBehavior);
             return this;
         }
 
@@ -1184,14 +1185,14 @@
      * @param intent Intent to retrieve the resize behavior from.
      * @return The resize behavior. If {@link CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX}
      *         is not set as part of the same intent, the value has no effect.
-     * @see CustomTabsIntent#EXTRA_ACTIVITY_RESIZE_BEHAVIOR
+     * @see CustomTabsIntent#EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR
      * @see CustomTabsIntent#ACTIVITY_HEIGHT_DEFAULT
      * @see CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE
      * @see CustomTabsIntent#ACTIVITY_HEIGHT_FIXED
      */
-    @ActivityResizeBehavior
+    @ActivityHeightResizeBehavior
     public static int getActivityResizeBehavior(@NonNull Intent intent) {
-        return intent.getIntExtra(EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+        return intent.getIntExtra(EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
                 CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT);
     }
 
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
index e495fc9..435e242 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
@@ -74,7 +74,7 @@
                 Uri requestedOrigin, boolean result, Bundle extras) {}
 
         @Override
-        public void onActivityResized(int size, Bundle extras) {}
+        public void onActivityResized(int height, int width, Bundle extras) {}
 
         @Override
         public IBinder asBinder() {
@@ -191,9 +191,9 @@
 
             @SuppressWarnings("NullAway")  // TODO: b/142938599
             @Override
-            public void onActivityResized(int size, @NonNull Bundle extras) {
+            public void onActivityResized(int height, int width, @NonNull Bundle extras) {
                 try {
-                    mCallbackBinder.onActivityResized(size, extras);
+                    mCallbackBinder.onActivityResized(height, width, extras);
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
                 }
diff --git a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
index 18e3aa6..1bba7b6 100644
--- a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
+++ b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
@@ -157,7 +157,7 @@
     }
 
     @Test
-    public void testActivityInitialFixedResizeBehavior() {
+    public void testActivityInitialFixedHeightResizeBehavior() {
         int heightFixedResizeBehavior = CustomTabsIntent.ACTIVITY_HEIGHT_FIXED;
         int initialActivityHeight = 200;
 
@@ -166,9 +166,10 @@
                 .build()
                 .intent;
 
-        assertEquals("The value of EXTRA_ACTIVITY_FIXED_HEIGHT should be ACTIVITY_HEIGHT_FIXED.",
+        assertEquals("The value of EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should be "
+                        + "ACTIVITY_HEIGHT_FIXED.",
                 heightFixedResizeBehavior,
-                intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+                intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
                         CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT));
         assertEquals("The height should be the same as the one that was set.",
                 initialActivityHeight,
@@ -182,7 +183,7 @@
     }
 
     @Test
-    public void testActivityInitialAdjustableResizeBehavior() {
+    public void testActivityInitialAdjustableHeightResizeBehavior() {
         int heightAdjustableResizeBehavior = CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE;
         int initialActivityHeight = 200;
 
@@ -191,10 +192,10 @@
                 .build()
                 .intent;
 
-        assertEquals("The value of EXTRA_ACTIVITY_FIXED_HEIGHT should be "
+        assertEquals("The value of EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should be "
                         + "ACTIVITY_HEIGHT_ADJUSTABLE.",
                 heightAdjustableResizeBehavior,
-                intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+                intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
                         CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT));
         assertEquals("The height should be the same as the one that was set.",
                 initialActivityHeight,
@@ -220,10 +221,10 @@
         assertEquals("The height should be the same as the one that was set.",
                 initialActivityHeight,
                 intent.getIntExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX, 0));
-        assertEquals("The value of EXTRA_ACTIVITY_RESIZE_BEHAVIOR should be "
+        assertEquals("The value of EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should be "
                         + "ACTIVITY_HEIGHT_DEFAULT.",
                 defaultResizeBehavior,
-                intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+                intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
                         CustomTabsIntent.ACTIVITY_HEIGHT_FIXED));
         assertEquals("The height returned by the getter should be the same.",
                 initialActivityHeight,
@@ -242,8 +243,8 @@
 
         assertFalse("The EXTRA_INITIAL_ACTIVITY_HEIGHT_PX should not be set.",
                 intent.hasExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX));
-        assertFalse("The EXTRA_ACTIVITY_RESIZE_BEHAVIOR should not be set.",
-                intent.hasExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR));
+        assertFalse("The EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should not be set.",
+                intent.hasExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR));
         assertEquals("The getter should return the default value.",
                 defaultInitialActivityHeight,
                 CustomTabsIntent.getInitialActivityHeightPx(intent));
diff --git a/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt b/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
index 5ad9a67..653ae96 100644
--- a/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
+++ b/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
@@ -88,9 +88,13 @@
         if (outDir == null || outDir == "") {
             outDir = File(projectDir, "../../out").normalize().toString()
         }
+        // --dependency-verification=off is set because we don't have to do validation of
+        // dependencies during these tests, it is already handled by the main build.
+        // Having it validate here breaks in androidx-studio-integration case where we
+        // might get new dependencies from AGP that are missinng signatures.
         GradleRunner.create()
             .withProjectDir(projectDir)
-            .withArguments("-Pandroidx.projects=$name", "tasks")
+            .withArguments("-Pandroidx.projects=$name", "tasks", "--dependency-verification=off")
             .withTestKitDir(File(outDir, ".gradle-testkit"))
             .build(); // fails the test if the build fails
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
index bc95bb6e..9a796c7e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
@@ -36,7 +36,6 @@
 import org.gradle.api.GradleException
 import org.gradle.api.Project
 import org.gradle.api.XmlProvider
-import org.gradle.api.attributes.DocsType
 import org.gradle.api.component.ComponentWithVariants
 import org.gradle.api.component.SoftwareComponent
 import org.gradle.api.component.SoftwareComponentFactory
@@ -383,10 +382,7 @@
 ) {
     val targetConfigurations = mutableSetOf<Configuration>()
     configurations.configureEach {
-        if (
-            it.attributes.getAttribute(DocsType.DOCS_TYPE_ATTRIBUTE)?.name == DocsType.SOURCES &&
-            it.name in names
-        ) {
+        if (it.name in names) {
             targetConfigurations.add(it)
             if (targetConfigurations.size == names.size) {
                 action(
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index a4a255a..39e7417 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -623,6 +623,11 @@
                 ":compose:material:material:icons:generator",
                 ":compose:material:material-icons-extended"
             ),
+            // Link glance-appwidget macrobenchmark and its target.
+            setOf(
+                ":glance:glance-appwidget:integration-tests:macrobenchmark",
+                ":glance:glance-appwidget:integration-tests:macrobenchmark-target"
+            ),
         )
 
         val IGNORED_PATHS = setOf(
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
index 808e43d..88efac3 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
@@ -182,7 +182,7 @@
     @Test
     fun correctAvailability_whenExtensionIsNotAvailable() {
         // Skips the test if extensions availability is disabled by quirk.
-        assumeFalse(ExtensionsTestUtil.extensionsDisabledByQuirk(lensFacing, extensionMode))
+        assumeFalse(ExtensionsTestUtil.extensionsDisabledByQuirk())
 
         extensionsManager = ExtensionsManager.getInstanceAsync(
             context,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index 57d7a47..9deb408 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -207,10 +207,7 @@
     /**
      * Returns whether extensions is disabled by quirk.
      */
-    public static boolean extensionsDisabledByQuirk(@CameraSelector.LensFacing int lensFacing,
-            @ExtensionMode.Mode int extensionMode) {
-
-        return new ExtensionDisabledValidator().shouldDisableExtension(
-                CameraUtil.getCameraIdWithLensFacing(lensFacing), extensionMode);
+    public static boolean extensionsDisabledByQuirk() {
+        return new ExtensionDisabledValidator().shouldDisableExtension();
     }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
index 7715f57..17a7a78 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
@@ -56,10 +56,8 @@
             new ExtensionDisabledValidator();
     private final AdvancedExtenderImpl mAdvancedExtenderImpl;
     private String mCameraId;
-    private final @ExtensionMode.Mode int mMode;
 
     public AdvancedVendorExtender(@ExtensionMode.Mode int mode) {
-        mMode = mode;
         try {
             switch (mode) {
                 case ExtensionMode.BOKEH:
@@ -101,7 +99,7 @@
     public boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
 
-        if (mExtensionDisabledValidator.shouldDisableExtension(cameraId, mMode)) {
+        if (mExtensionDisabledValidator.shouldDisableExtension()) {
             return false;
         }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
index 2a9f2d0..37b5610 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
@@ -123,7 +123,7 @@
     public boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
 
-        if (mExtensionDisabledValidator.shouldDisableExtension(cameraId, mMode)) {
+        if (mExtensionDisabledValidator.shouldDisableExtension()) {
             return false;
         }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
index 6b58434..ec3f327 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.extensions.internal;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.Logger;
@@ -75,6 +76,38 @@
         return getInstance().isAdvancedExtenderSupportedInternal();
     }
 
+    /**
+     * Check if the Runtime Version meets the minimum compatible version requirement. This implies
+     * that the runtime version is equal to or newer than the version.
+     *
+     * <p> The compatible version is comprised of the major and minor version numbers. The patch
+     * number is ignored.
+     *
+     * @param version The minimum compatible version required
+     * @return True if the Runtime version meets the minimum version requirement and False
+     * otherwise.
+     */
+    public static boolean isMinimumCompatibleVersion(@NonNull Version version) {
+        return ExtensionVersion.getRuntimeVersion()
+                .compareTo(version.getMajor(), version.getMinor()) >= 0;
+    }
+
+    /**
+     * Check if the Runtime Version meets the maximum compatible version requirement. This implies
+     * that the runtime version is equal to or older than the version.
+     *
+     * <p> The compatible version is comprised of the major and minor version numbers. The patch
+     * number is ignored.
+     *
+     * @param version The maximum compatible version required
+     * @return True if the Runtime version meets the maximum version requirement and False
+     * otherwise.
+     */
+    public static boolean isMaximumCompatibleVersion(@NonNull Version version) {
+        return ExtensionVersion.getRuntimeVersion()
+                .compareTo(version.getMajor(), version.getMinor()) <= 0;
+    }
+
     abstract boolean isAdvancedExtenderSupportedInternal();
 
     /**
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
index c9dd6cd..992962c 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
@@ -18,46 +18,43 @@
 
 import android.os.Build;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.Quirk;
-import androidx.camera.extensions.ExtensionMode;
 import androidx.camera.extensions.internal.ExtensionVersion;
 import androidx.camera.extensions.internal.Version;
 
 
 /**
  * <p>QuirkSummary
- *     Bug Id: b/199408131, b/214130117
- *     Description: Quirk required to disable extension for some devices. An example is that
- *                  Pixel 5's availability check result of the basic extension interface should
- *                  be false, but it actually returns true. Therefore, force disable Basic
- *                  Extender capability on the device. Another example is that Motorola razr 5G's
- *                  availability check results of both back and front camera are true, but it
- *                  will cause the black preview screen issue. Therefore, force disable the bokeh
- *                  mode on the device.
- *     Device(s): Pixel 5, Motorola razr 5G
- *     @see androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator
+ * Bug Id: b/199408131, b/214130117, b/255956506
+ * Description: Quirk required to disable extension for some devices. An example is that
+ * Pixel 5's availability check result of the basic extension interface should
+ * be false, but it actually returns true. Therefore, force disable Basic
+ * Extender capability on the device. Another example is to ensure Motorola devices meet the
+ * minimum quality requirements for camera extensions support. Common issues encountered with
+ * Motorola extensions include: Bokeh not supported on some devices, SurfaceView not supported,
+ * Image doesn't appear after taking a picture, Preview is pauses after resuming.
+ * Device(s): Pixel 5, Motorola
+ *
+ * @see androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class ExtensionDisabledQuirk implements Quirk {
-    private boolean mIsAdvancedInterface = isAdvancedExtenderSupported();
+    private final boolean mIsAdvancedInterface = isAdvancedExtenderSupported();
 
     static boolean load() {
-        return isPixel5() || isMotoRazr5G() || isAdvancedExtenderSupported();
+        return isPixel5() || isMoto() || isAdvancedExtenderSupported();
     }
 
     /**
      * Checks whether extension should be disabled.
      */
-    public boolean shouldDisableExtension(@NonNull String cameraId,
-            @ExtensionMode.Mode int extensionMode) {
+    public boolean shouldDisableExtension() {
         if (isPixel5() && !mIsAdvancedInterface) {
             // 1. Disables Pixel 5's Basic Extender capability.
             return true;
-        } else if (isMotoRazr5G() && ("0".equals(cameraId) || "1".equals(cameraId)) && (
-                ExtensionMode.BOKEH == extensionMode)) {
-            // 2. Disables Motorola Razr 5G's bokeh capability.
+        } else if (isMoto() && ExtensionVersion.isMaximumCompatibleVersion(Version.VERSION_1_1)) {
+            // 2. Disables Motorola extensions capability for version 1.1 and older.
             return true;
         }
 
@@ -68,14 +65,12 @@
         return "google".equalsIgnoreCase(Build.BRAND) && "redfin".equalsIgnoreCase(Build.DEVICE);
     }
 
-    private static boolean isMotoRazr5G() {
-        return "motorola".equalsIgnoreCase(Build.BRAND) && "smith".equalsIgnoreCase(Build.DEVICE);
+    private static boolean isMoto() {
+        return "motorola".equalsIgnoreCase(Build.BRAND);
     }
 
     private static boolean isAdvancedExtenderSupported() {
-        if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_2) < 0) {
-            return false;
-        }
-        return ExtensionVersion.isAdvancedExtenderSupported();
+        return ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_2)
+                && ExtensionVersion.isAdvancedExtenderSupported();
     }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
index 3b9bcd1..166eabf 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
@@ -16,9 +16,7 @@
 
 package androidx.camera.extensions.internal.compat.workaround;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
-import androidx.camera.extensions.ExtensionMode;
 import androidx.camera.extensions.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.extensions.internal.compat.quirk.ExtensionDisabledQuirk;
 
@@ -40,8 +38,7 @@
     /**
      * Checks whether extension should be disabled.
      */
-    public boolean shouldDisableExtension(@NonNull String cameraId,
-            @ExtensionMode.Mode int extensionMode) {
-        return mQuirk != null && mQuirk.shouldDisableExtension(cameraId, extensionMode);
+    public boolean shouldDisableExtension() {
+        return mQuirk != null && mQuirk.shouldDisableExtension();
     }
 }
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMaximumCompatibleTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMaximumCompatibleTest.kt
new file mode 100644
index 0000000..b21dea5
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMaximumCompatibleTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.internal
+
+import android.os.Build
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.resetSingleton
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.setTestApiVersion
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+    minSdk = Build.VERSION_CODES.LOLLIPOP,
+    instrumentedPackages = arrayOf("androidx.camera.extensions.internal")
+)
+class ExtensionVersionMaximumCompatibleTest(private val config: TestConfig) {
+
+    @Before
+    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
+    fun setUp() {
+        val field = VersionName::class.java.getDeclaredField("CURRENT")
+        field.isAccessible = true
+        field[null] = VersionName(config.targetVersion)
+    }
+
+    @After
+    fun tearDown() {
+        resetSingleton(ExtensionVersion::class.java, "sExtensionVersion")
+    }
+
+    @Test
+    fun isMaximumCompatibleVersion() {
+        setTestApiVersion(config.targetVersion)
+
+        val version = Version.parse(config.maximumCompatibleVersion)!!
+        assertThat(ExtensionVersion.isMaximumCompatibleVersion(version))
+            .isEqualTo(config.expectedResult)
+    }
+
+    data class TestConfig(
+        val targetVersion: String,
+        val maximumCompatibleVersion: String,
+        val expectedResult: Boolean
+    )
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+        fun createTestSet(): List<TestConfig> {
+            return listOf(
+                TestConfig("1.1.0", "1.1.0", true),
+                TestConfig("1.1.0", "1.2.0", true),
+                TestConfig("1.1.0", "1.0.0", false),
+                TestConfig("1.1.0", "0.9.0", false),
+
+                // Test to ensure the patch version is ignored
+                TestConfig("1.2.1", "1.2.0", true),
+                TestConfig("1.2.0", "1.2.1", true),
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMinimumCompatibleTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMinimumCompatibleTest.kt
new file mode 100644
index 0000000..d92c03db1
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMinimumCompatibleTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.internal
+
+import android.os.Build
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.resetSingleton
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.setTestApiVersion
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+    minSdk = Build.VERSION_CODES.LOLLIPOP,
+    instrumentedPackages = arrayOf("androidx.camera.extensions.internal")
+)
+class ExtensionVersionMinimumCompatibleTest(private val config: TestConfig) {
+
+    @Before
+    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
+    fun setUp() {
+        val field = VersionName::class.java.getDeclaredField("CURRENT")
+        field.isAccessible = true
+        field[null] = VersionName(config.targetVersion)
+    }
+
+    @After
+    fun tearDown() {
+        resetSingleton(ExtensionVersion::class.java, "sExtensionVersion")
+    }
+
+    @Test
+    fun isMinimumCompatibleVersion() {
+        setTestApiVersion(config.targetVersion)
+        val version = Version.parse(config.minimumCompatibleVersion)!!
+        assertThat(ExtensionVersion.isMinimumCompatibleVersion(version))
+            .isEqualTo(config.expectedResult)
+    }
+
+    data class TestConfig(
+        val targetVersion: String,
+        val minimumCompatibleVersion: String,
+        val expectedResult: Boolean
+    )
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+        fun createTestSet(): List<TestConfig> {
+            return listOf(
+                TestConfig("1.1.0", "1.1.0", true),
+                TestConfig("1.1.0", "1.0.0", true),
+                TestConfig("1.1.0", "1.2.0", false),
+
+                // Test to ensure the patch version is ignored
+                TestConfig("1.1.1", "1.1.0", true),
+                TestConfig("1.1.0", "1.1.1", true),
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
index a70923e..d34b6c6 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
@@ -17,7 +17,6 @@
 package androidx.camera.extensions.internal.compat.workaround
 
 import android.os.Build
-import androidx.camera.extensions.ExtensionMode
 import androidx.camera.extensions.internal.ExtensionVersion
 import androidx.camera.extensions.internal.util.ExtensionsTestUtil.resetSingleton
 import androidx.camera.extensions.internal.util.ExtensionsTestUtil.setTestApiVersionAndAdvancedExtender
@@ -41,7 +40,7 @@
 
     @Before
     fun setUp() {
-        setTestApiVersionAndAdvancedExtender("1.2.0", config.isAdvancedInterface)
+        setTestApiVersionAndAdvancedExtender(config.version, config.isAdvancedInterface)
     }
 
     @After
@@ -56,19 +55,13 @@
         ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", config.device)
 
         val validator = ExtensionDisabledValidator()
-        assertThat(
-            validator.shouldDisableExtension(
-                config.cameraId,
-                config.extensionMode
-            )
-        ).isEqualTo(config.shouldDisableExtension)
+        assertThat(validator.shouldDisableExtension()).isEqualTo(config.shouldDisableExtension)
     }
 
     class TestConfig(
         val brand: String,
         val device: String,
-        val cameraId: String,
-        val extensionMode: Int,
+        val version: String,
         val isAdvancedInterface: Boolean,
         val shouldDisableExtension: Boolean
     )
@@ -79,36 +72,24 @@
         fun createTestSet(): List<TestConfig> {
             return listOf(
                 // Pixel 5 extension capability is disabled on basic extender
-                TestConfig("Google", "Redfin", "0", ExtensionMode.BOKEH, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.HDR, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.NIGHT, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.FACE_RETOUCH, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.AUTO, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.BOKEH, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.HDR, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.NIGHT, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.FACE_RETOUCH, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.AUTO, false, true),
+                TestConfig("Google", "Redfin", "1.2.0", false, true),
 
                 // Pixel 5 extension capability is enabled on advanced extender
-                TestConfig("Google", "Redfin", "0", ExtensionMode.NIGHT, true, false),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.NIGHT, true, false),
+                TestConfig("Google", "Redfin", "1.2.0", true, false),
 
-                // Motorola Razr 5G bokeh mode is disabled. Other extension modes should still work.
-                TestConfig("Motorola", "Smith", "0", ExtensionMode.BOKEH, false, true),
-                TestConfig("Motorola", "Smith", "0", ExtensionMode.HDR, false, false),
-                TestConfig("Motorola", "Smith", "1", ExtensionMode.BOKEH, false, true),
-                TestConfig("Motorola", "Smith", "1", ExtensionMode.HDR, false, false),
-                TestConfig("Motorola", "Smith", "2", ExtensionMode.BOKEH, false, false),
-                TestConfig("Motorola", "Smith", "2", ExtensionMode.HDR, false, false),
+                // All Motorola devices should be disabled for version 1.1.0 and older.
+                TestConfig("Motorola", "Smith", "1.1.0", false, true),
+                TestConfig("Motorola", "Hawaii P", "1.1.0", false, true),
+
+                // Make sure Motorola device would still be enabled for newer versions
+                // Motorola doesn't support this today but making sure there is a path to enable
+                TestConfig("Motorola", "Hawaii P", "1.2.0", false, false),
 
                 // Other cases should be kept normal.
-                TestConfig("", "", "0", ExtensionMode.BOKEH, false, false),
-                TestConfig("", "", "1", ExtensionMode.BOKEH, false, false),
+                TestConfig("", "", "1.2.0", false, false),
 
                 // Advanced extender is enabled for all devices
-                TestConfig("", "", "0", ExtensionMode.BOKEH, true, false),
-                TestConfig("", "", "1", ExtensionMode.BOKEH, true, false),
+                TestConfig("", "", "1.2.0", true, false),
             )
         }
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recording.java b/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
index 9002f39..c4c874b 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
@@ -16,8 +16,11 @@
 
 package androidx.camera.video;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.camera.core.impl.utils.CloseGuardHelper;
 import androidx.core.util.Consumer;
 import androidx.core.util.Preconditions;
@@ -46,7 +49,7 @@
 public final class Recording implements AutoCloseable {
 
     // Indicates the recording has been explicitly stopped by users.
-    private final AtomicBoolean mIsStopped = new AtomicBoolean(false);
+    private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
     private final Recorder mRecorder;
     private final long mRecordingId;
     private final OutputOptions mOutputOptions;
@@ -59,7 +62,7 @@
         mOutputOptions = options;
 
         if (finalizedOnCreation) {
-            mIsStopped.set(true);
+            mIsClosed.set(true);
         } else {
             mCloseGuard.open("stop");
         }
@@ -119,7 +122,7 @@
      * {@link #close()} or {@link #stop()}.
      */
     public void pause() {
-        if (mIsStopped.get()) {
+        if (mIsClosed.get()) {
             throw new IllegalStateException("The recording has been stopped.");
         }
         mRecorder.pause(this);
@@ -138,43 +141,44 @@
      * {@link #close()} or {@link #stop()}.
      */
     public void resume() {
-        if (mIsStopped.get()) {
+        if (mIsClosed.get()) {
             throw new IllegalStateException("The recording has been stopped.");
         }
         mRecorder.resume(this);
     }
 
     /**
-     * Stops the recording.
+     * Stops the recording, as if calling {@link #close()}.
      *
-     * <p>Once stopped, all methods for controlling the state of this recording besides
-     * {@code stop()} or {@link #close()} will throw an {@link IllegalStateException}.
-     *
-     * <p>Once an active recording has been stopped, the next recording can be started with
-     * {@link PendingRecording#start(Executor, Consumer)}.
-     *
-     * <p>This method is idempotent; if the recording has already been stopped or has been
-     * finalized internally, calling {@code stop()} is a no-op.
+     * <p>This method is equivalent to calling {@link #close()}.
      */
     public void stop() {
-        mCloseGuard.close();
-        if (mIsStopped.getAndSet(true)) {
-            return;
-        }
-        mRecorder.stop(this);
+        close();
     }
 
     /**
-     * Close this recording, as if calling {@link #stop()}.
+     * Close this recording.
+     *
+     * <p>Once {@link #stop()} or {@code close()} called, all methods for controlling the state of
+     * this recording besides {@link #stop()} or {@code close()} will throw an
+     * {@link IllegalStateException}.
+     *
+     * <p>Once an active recording has been closed, the next recording can be started with
+     * {@link PendingRecording#start(Executor, Consumer)}.
+     *
+     * <p>This method is idempotent; if the recording has already been closed or has been
+     * finalized internally, calling {@link #stop()} or {@code close()} is a no-op.
      *
      * <p>This method is invoked automatically on active recording instances managed by the {@code
      * try-with-resources} statement.
-     *
-     * <p>This method is equivalent to calling {@link #stop()}.
      */
     @Override
     public void close() {
-        stop();
+        mCloseGuard.close();
+        if (mIsClosed.getAndSet(true)) {
+            return;
+        }
+        mRecorder.stop(this);
     }
 
     @Override
@@ -192,4 +196,21 @@
     long getRecordingId() {
         return mRecordingId;
     }
+
+    /**
+     * Returns whether the recording is closed.
+     *
+     * <p>The returned value does not reflect the state of the recording; it only reflects
+     * whether {@link #stop()} or {@link #close()} was called on this object.
+     *
+     * <p>The state of the recording should be checked from the listener passed to
+     * {@link PendingRecording#start(Executor, Consumer)}. Once the active recording is
+     * stopped, a {@link VideoRecordEvent.Finalize} event will be sent to the listener.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public boolean isClosed() {
+        return mIsClosed.get();
+    }
 }
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
index 7cdee86..5fbc9a3 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -39,16 +40,13 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 28)
-class AdvancedExtenderValidationTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
-    private val validation = AdvancedExtenderValidation(cameraId, extensionMode)
+class AdvancedExtenderValidationTest(config: CameraIdExtensionModePair) {
+    private val validation = AdvancedExtenderValidation(config.cameraId, config.extensionMode)
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt
index 31fb57d..168e040 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt
@@ -34,6 +34,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.VERIFICATION_TARGET_IMAGE_ANALYSIS
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.VERIFICATION_TARGET_IMAGE_CAPTURE
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.VERIFICATION_TARGET_PREVIEW
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -67,10 +68,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class BindUnbindUseCasesStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class BindUnbindUseCasesStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -96,6 +94,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -133,8 +132,8 @@
         @JvmField val stressTest = StressTestRule()
 
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt
index 1475998..d294dbc 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt
@@ -31,6 +31,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.launchCameraExtensionsActivity
 import androidx.camera.integration.extensions.util.HOME_TIMEOUT_MS
 import androidx.camera.integration.extensions.util.waitForPreviewViewStreaming
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -60,10 +61,7 @@
 @SmallTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class ImageCaptureExtenderValidationTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class ImageCaptureExtenderValidationTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -86,6 +84,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -119,8 +118,8 @@
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
@@ -133,8 +132,8 @@
         // Creates the ImageCaptureExtenderImpl to retrieve the target format/resolutions pair list
         // from vendor library for the target effect mode.
         val impl = CameraXExtensionsTestUtil.createImageCaptureExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
 
@@ -149,8 +148,8 @@
         // Creates the ImageCaptureExtenderImpl to check that onPresetSession() returns null when
         // API level is older than 28.
         val impl = CameraXExtensionsTestUtil.createImageCaptureExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
         assertThat(impl.onPresetSession()).isNull()
@@ -166,7 +165,7 @@
         // the getEstimatedCaptureLatencyRange function.
         val latencyInfo = extensionsManager.getEstimatedCaptureLatencyRange(
             baseCameraSelector,
-            extensionMode
+            config.extensionMode
         )
 
         // Calls bind to lifecycle to get the selected camera
@@ -179,7 +178,7 @@
 
         // Creates ImageCaptureExtenderImpl directly to retrieve the capture latency range info
         val impl = CameraXExtensionsTestUtil.createImageCaptureExtenderImpl(
-            extensionMode,
+            config.extensionMode,
             cameraId,
             characteristics
         )
@@ -201,7 +200,10 @@
             setOrientationNatural()
         }
 
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(
+            config.cameraId,
+            config.extensionMode
+        )
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt
index 9cf35f4..941ad66 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt
@@ -25,6 +25,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.launchCameraExtensionsActivity
 import androidx.camera.integration.extensions.util.HOME_TIMEOUT_MS
 import androidx.camera.integration.extensions.util.takePictureAndWaitForImageSavedIdle
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraUtil.PreTestCameraIdList
@@ -48,7 +49,7 @@
  */
 @LargeTest
 @RunWith(Parameterized::class)
-class ImageCaptureTest(private val cameraId: String, private val extensionMode: Int) {
+class ImageCaptureTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -62,7 +63,7 @@
     private val context = ApplicationProvider.getApplicationContext<Context>()
 
     companion object {
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -87,7 +88,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
-        assumeExtensionModeSupported(extensionsManager, cameraId, extensionMode)
+        assumeExtensionModeSupported(extensionsManager, config.cameraId, config.extensionMode)
     }
 
     @After
@@ -114,7 +115,7 @@
      */
     @Test
     fun takePictureWithExtensionMode() {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt
index 51c1624..04404e4 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt
@@ -28,6 +28,7 @@
 import androidx.camera.integration.extensions.util.takePictureAndWaitForImageSavedIdle
 import androidx.camera.integration.extensions.util.waitForPreviewViewIdle
 import androidx.camera.integration.extensions.util.waitForPreviewViewStreaming
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraUtil.PreTestCameraIdList
@@ -57,10 +58,7 @@
  */
 @LargeTest
 @RunWith(Parameterized::class)
-class LifecycleStatusChangeStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class LifecycleStatusChangeStressTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -74,7 +72,7 @@
     private val context = ApplicationProvider.getApplicationContext<Context>()
 
     companion object {
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -95,8 +93,8 @@
         // Checks whether the extension mode can be supported first before launching the activity.
         CameraXExtensionsTestUtil.assumeExtensionModeSupported(
             extensionsManager,
-            cameraId,
-            extensionMode
+            config.cameraId,
+            config.extensionMode
         )
 
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
@@ -147,7 +145,7 @@
         verificationTarget: Int,
         repeatCount: Int = CameraXExtensionsTestUtil.getStressTestRepeatingCount()
     ) {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
@@ -188,7 +186,7 @@
         verificationTarget: Int,
         repeatCount: Int = CameraXExtensionsTestUtil.getStressTestRepeatingCount()
     ) {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt
index d0a1279..44aa1be 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt
@@ -27,6 +27,7 @@
 import androidx.camera.core.UseCase
 import androidx.camera.extensions.ExtensionsManager
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -56,10 +57,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class OpenCloseCameraStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class OpenCloseCameraStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -79,6 +77,7 @@
     @Before
     fun setUp(): Unit = runBlocking {
         assumeTrue(CameraXExtensionsTestUtil.isTargetDeviceAvailableForExtensions())
+        val (cameraId, extensionMode) = config
         cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
         extensionsManager = ExtensionsManager.getInstanceAsync(
             context,
@@ -125,8 +124,8 @@
         @JvmField val stressTest = StressTestRule()
 
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt
index 5dedbdf..902b8ad 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt
@@ -41,6 +41,7 @@
 import androidx.camera.extensions.ExtensionMode
 import androidx.camera.extensions.ExtensionsManager
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -69,10 +70,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class OpenCloseCaptureSessionStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class OpenCloseCaptureSessionStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -101,6 +99,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -175,7 +174,7 @@
                 val extensionEnabledCameraEventMonitorCameraSelector =
                     getExtensionsCameraEventMonitorCameraSelector(
                         extensionsManager,
-                        extensionMode,
+                        config.extensionMode,
                         baseCameraSelector
                     )
 
@@ -205,8 +204,8 @@
         @JvmField val stressTest = StressTestRule()
 
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
 
         /**
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt
index c0a25f0..9cf7c98 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt
@@ -29,6 +29,7 @@
 import androidx.camera.extensions.internal.ExtensionVersion
 import androidx.camera.extensions.internal.Version
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -53,10 +54,7 @@
 @SmallTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class PreviewExtenderValidationTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class PreviewExtenderValidationTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -79,6 +77,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -110,8 +109,8 @@
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
@@ -124,8 +123,8 @@
         // Creates the ImageCaptureExtenderImpl to retrieve the target format/resolutions pair list
         // from vendor library for the target effect mode.
         val impl = CameraXExtensionsTestUtil.createPreviewExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
 
@@ -140,8 +139,8 @@
         // Creates the ImageCaptureExtenderImpl to check that onPresetSession() returns null when
         // API level is older than 28.
         val impl = CameraXExtensionsTestUtil.createPreviewExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
         assertThat(impl.onPresetSession()).isNull()
@@ -150,8 +149,8 @@
     @Test
     fun returnCorrectProcessor() {
         val impl = CameraXExtensionsTestUtil.createPreviewExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt
index f96fbed..571c3bf 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt
@@ -40,6 +40,7 @@
 import androidx.camera.extensions.ExtensionMode
 import androidx.camera.extensions.ExtensionsManager
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -73,10 +74,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class PreviewProcessorTimestampTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class PreviewProcessorTimestampTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -155,6 +153,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -191,7 +190,7 @@
             val timestampExtensionEnabledCameraSelector =
                 getTimestampExtensionEnabledCameraSelector(
                     extensionsManager,
-                    extensionMode,
+                    config.extensionMode,
                     baseCameraSelector
                 )
 
@@ -236,8 +235,8 @@
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
 
         /**
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt
index 03371e2..8154c16 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt
@@ -24,6 +24,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.launchCameraExtensionsActivity
 import androidx.camera.integration.extensions.util.HOME_TIMEOUT_MS
 import androidx.camera.integration.extensions.util.waitForPreviewViewStreaming
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -48,7 +49,7 @@
  */
 @LargeTest
 @RunWith(Parameterized::class)
-class PreviewTest(private val cameraId: String, private val extensionMode: Int) {
+class PreviewTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -60,7 +61,7 @@
     private lateinit var extensionsManager: ExtensionsManager
 
     companion object {
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -86,7 +87,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
-        assumeExtensionModeSupported(extensionsManager, cameraId, extensionMode)
+        assumeExtensionModeSupported(extensionsManager, config.cameraId, config.extensionMode)
     }
 
     @After
@@ -114,7 +115,7 @@
      */
     @Test
     fun previewWithExtensionModeCanEnterStreamingState() {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
@@ -128,7 +129,7 @@
             ApplicationProvider.getApplicationContext(),
             extensionsManager,
             cameraId,
-            extensionMode
+            config.extensionMode
         )
         assumeTrue(
             "Cannot find next camera id that supports extensions mode($extensionsMode)",
@@ -140,8 +141,8 @@
      */
     @Test
     fun previewCanEnterStreamingStateAfterSwitchingCamera() {
-        assumeNextCameraIdExtensionModeSupported(cameraId, extensionMode)
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        assumeNextCameraIdExtensionModeSupported(config.cameraId, config.extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
index 2ba7a62..f689467 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
@@ -30,6 +30,7 @@
 import androidx.camera.integration.extensions.util.waitForImageSavedIdle
 import androidx.camera.integration.extensions.util.waitForPreviewIdle
 import androidx.camera.integration.extensions.utils.Camera2ExtensionsUtil.isCamera2ExtensionModeSupported
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CoreAppTestUtil
 import androidx.camera.testing.LabTestRule
@@ -58,10 +59,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsActivityTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsActivityTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -101,7 +99,7 @@
         @ClassRule
         @JvmField val stressTest = StressTestRule()
 
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -110,8 +108,7 @@
     @Test
     fun checkPreviewUpdated() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -125,8 +122,7 @@
     @Test
     fun canCaptureSingleImage() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -140,8 +136,7 @@
     @Test
     fun checkPreviewUpdated_afterPauseResume() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -162,8 +157,7 @@
     @Test
     fun canCaptureImage_afterPauseResume() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+           config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -187,8 +181,7 @@
     @Test
     fun canCaptureMultipleImages() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -201,9 +194,9 @@
     }
 
     private fun launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-        cameraId: String,
-        extensionMode: Int
+        config: CameraIdExtensionModePair
     ): ActivityScenario<Camera2ExtensionsActivity> {
+        val (cameraId, extensionMode) = config
         val context = ApplicationProvider.getApplicationContext<Context>()
         assumeTrue(isCamera2ExtensionModeSupported(context, cameraId, extensionMode))
         val intent = context.packageManager
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsImageCaptureStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsImageCaptureStressTest.kt
new file mode 100644
index 0000000..f9d8b9d
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsImageCaptureStressTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.camera2extensions
+
+import android.content.Context
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionSession
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.params.OutputConfiguration
+import android.media.ImageReader
+import android.view.Surface
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.assumeCameraExtensionSupported
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.createCaptureImageReader
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.openCameraDevice
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.openExtensionSession
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.takePicture
+import androidx.camera.integration.extensions.util.assertImageIsValid
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.StressTestRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Stress test to verify that the camera can successfully capture images for all supported
+ * extension modes for each cameras ID.
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = 31)
+class Camera2ExtensionsImageCaptureStressTest(private val config: CameraIdExtensionModePair) {
+    @get:Rule
+    val useCamera =
+        CameraUtil.grantCameraPermissionAndPreTest(
+            CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
+        )
+
+    companion object {
+        @ClassRule
+        @JvmField val stressTest = StressTestRule()
+
+        @Parameterized.Parameters(name = "config = {0}")
+        @JvmStatic
+        fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
+    }
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+
+    private lateinit var cameraDevice: CameraDevice
+    private lateinit var imageReader: ImageReader
+    private lateinit var captureSurface: Surface
+    private lateinit var extensionSession: CameraExtensionSession
+
+    @Before
+    fun setUp(): Unit = runBlocking {
+        assumeTrue(Camera2ExtensionsTestUtil.isTargetDeviceExcludedForExtensionsTest())
+
+        val (cameraId, extensionMode) = config
+
+        val extensionsCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId)
+        assumeCameraExtensionSupported(extensionMode, extensionsCharacteristics)
+
+        cameraDevice = openCameraDevice(cameraManager, cameraId)
+        imageReader = createCaptureImageReader(extensionsCharacteristics, extensionMode)
+        captureSurface = imageReader.surface
+        val outputConfigurationCapture = OutputConfiguration(captureSurface)
+        extensionSession = openExtensionSession(
+            cameraDevice,
+            extensionMode,
+            listOf(outputConfigurationCapture)
+        )
+        assertThat(extensionSession).isNotNull()
+    }
+
+    @After
+    fun tearDown() {
+        if (::extensionSession.isInitialized) {
+            extensionSession.close()
+        }
+
+        if (::cameraDevice.isInitialized) {
+            cameraDevice.close()
+        }
+
+        if (::imageReader.isInitialized) {
+            imageReader.close()
+        }
+
+        if (::captureSurface.isInitialized) {
+            captureSurface.release()
+        }
+    }
+
+    @Test
+    fun captureImage(): Unit = runBlocking {
+        repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
+            val image = takePicture(cameraDevice, extensionSession, imageReader)
+            assertThat(image).isNotNull()
+
+            image?.let {
+                assertThat(it.timestamp).isGreaterThan(0)
+                assertImageIsValid(it, imageReader.width, imageReader.height)
+            }
+
+            image!!.close()
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt
index c42d674..8c9f89d 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt
@@ -21,6 +21,7 @@
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.assertCanOpenExtensionsSession
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.StressTestRule
 import androidx.test.core.app.ApplicationProvider
@@ -38,10 +39,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsOpenCloseStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsOpenCloseStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -51,7 +49,7 @@
         @ClassRule
         @JvmField val stressTest = StressTestRule()
 
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -65,6 +63,7 @@
     }
     @Test
     fun openCloseExtensionSession(): Unit = runBlocking {
+        val (cameraId, extensionMode) = config
         repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
             assertCanOpenExtensionsSession(cameraManager, cameraId, extensionMode)
         }
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt
index 9756e72..bc0dfdd 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt
@@ -22,6 +22,7 @@
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.assertCanOpenExtensionsSession
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.findNextSupportedCameraId
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.StressTestRule
 import androidx.test.core.app.ApplicationProvider
@@ -39,10 +40,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsSwitchCameraStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsSwitchCameraStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -52,7 +50,7 @@
         @ClassRule
         @JvmField val stressTest = StressTestRule()
 
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -67,6 +65,7 @@
 
     @Test
     fun switchCameras(): Unit = runBlocking {
+        val (cameraId, extensionMode) = config
         val nextCameraId = findNextSupportedCameraId(context, cameraId, extensionMode)
         assumeTrue(nextCameraId != null)
         repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt
index 47b7114..6cffd8f 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt
@@ -22,6 +22,7 @@
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.EXTENSION_NOT_FOUND
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.findNextEffectMode
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.StressTestRule
 import androidx.test.core.app.ApplicationProvider
@@ -39,10 +40,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsSwitchModeStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsSwitchModeStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -68,6 +66,7 @@
 
     @Test
     fun switchModes(): Unit = runBlocking {
+        val (cameraId, extensionMode) = config
         val nextMode = findNextEffectMode(context, cameraId, extensionMode)
         assumeTrue(nextMode != EXTENSION_NOT_FOUND)
         repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
index 5a32a44..429f3c8 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CameraAccessException
 import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionCharacteristics
 import android.hardware.camera2.CameraExtensionSession
 import android.hardware.camera2.CameraManager
 import android.hardware.camera2.CaptureRequest
@@ -37,6 +38,7 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.integration.extensions.utils.Camera2ExtensionsUtil.AVAILABLE_CAMERA2_EXTENSION_MODES
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.LabTestRule
 import androidx.camera.testing.SurfaceTextureProvider
@@ -73,12 +75,10 @@
      * Gets a list of all camera id and extension mode combinations.
      */
     @JvmStatic
-    fun getAllCameraIdExtensionModeCombinations(): List<Array<Any>> =
-        arrayListOf<Array<Any>>().apply {
-            CameraUtil.getBackwardCompatibleCameraIdListOrThrow().forEach { cameraId ->
-                AVAILABLE_CAMERA2_EXTENSION_MODES.forEach { mode ->
-                    add(arrayOf(cameraId, mode))
-                }
+    fun getAllCameraIdExtensionModeCombinations(): List<CameraIdExtensionModePair> =
+        CameraUtil.getBackwardCompatibleCameraIdListOrThrow().flatMap { cameraId ->
+            AVAILABLE_CAMERA2_EXTENSION_MODES.map { extensionMode ->
+                CameraIdExtensionModePair(cameraId, extensionMode)
             }
         }
 
@@ -89,15 +89,7 @@
         verifyOutput: Boolean = false
     ) {
         val extensionsCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId)
-        assumeTrue(extensionsCharacteristics.supportedExtensions.contains(extensionMode))
-        assumeTrue(
-            extensionsCharacteristics
-                .getExtensionSupportedSizes(extensionMode, SurfaceTexture::class.java).isNotEmpty()
-        )
-        assumeTrue(
-            extensionsCharacteristics
-                .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG).isNotEmpty()
-        )
+        assumeCameraExtensionSupported(extensionMode, extensionsCharacteristics)
 
         // Preview surface
         val previewSize = extensionsCharacteristics
@@ -120,11 +112,7 @@
         val previewSurface = Surface(surfaceTextureHolder.surfaceTexture)
 
         // Still capture surface
-        val captureSize = extensionsCharacteristics
-            .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG)
-            .maxBy { it.width * it.height }
-        val imageReader = ImageReader
-            .newInstance(captureSize.width, captureSize.height, ImageFormat.JPEG, 2)
+        val imageReader = createCaptureImageReader(extensionsCharacteristics, extensionMode)
         val captureSurface = imageReader.surface
 
         val cameraDevice = openCameraDevice(cameraManager, cameraId)
@@ -198,9 +186,39 @@
     }
 
     /**
+     * Check if the device supports the [extensionMode] and other extension specific characteristics
+     * required for testing. Halt the test if any criteria is not satisfied.
+     */
+    fun assumeCameraExtensionSupported(
+        extensionMode: Int,
+        extensionsCharacteristics: CameraExtensionCharacteristics
+    ) {
+        assumeTrue(extensionsCharacteristics.supportedExtensions.contains(extensionMode))
+        assumeTrue(
+            extensionsCharacteristics
+                .getExtensionSupportedSizes(extensionMode, SurfaceTexture::class.java).isNotEmpty()
+        )
+        assumeTrue(
+            extensionsCharacteristics
+                .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG).isNotEmpty()
+        )
+    }
+
+    fun createCaptureImageReader(
+        extensionsCharacteristics: CameraExtensionCharacteristics,
+        extensionMode: Int
+    ): ImageReader {
+        val captureSize = extensionsCharacteristics
+            .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG)
+            .maxBy { it.width * it.height }
+        return ImageReader
+            .newInstance(captureSize.width, captureSize.height, ImageFormat.JPEG, 2)
+    }
+
+    /**
      * Open the camera device and return the [CameraDevice] instance.
      */
-    private suspend fun openCameraDevice(
+    suspend fun openCameraDevice(
         cameraManager: CameraManager,
         cameraId: String
     ): CameraDevice {
@@ -229,7 +247,7 @@
     /**
      * Open the [CameraExtensionSession] and return the instance.
      */
-    private suspend fun openExtensionSession(
+    suspend fun openExtensionSession(
         cameraDevice: CameraDevice,
         extensionMode: Int,
         outputConfigs: List<OutputConfiguration>
@@ -257,7 +275,11 @@
         return deferred.await()
     }
 
-    private suspend fun takePicture(
+    /**
+     * Take a picture with the provided [session] and output the contents to the [imageReader]. The
+     * latest image written to the [imageReader] is returned.
+     */
+    suspend fun takePicture(
         cameraDevice: CameraDevice,
         session: CameraExtensionSession,
         imageReader: ImageReader
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
index 70f7440..09a7be9 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
@@ -49,6 +49,7 @@
 import androidx.camera.extensions.internal.Version
 import androidx.camera.integration.extensions.CameraExtensionsActivity
 import androidx.camera.integration.extensions.IntentExtraKey
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil.createCameraSelectorById
 import androidx.camera.integration.extensions.utils.ExtensionModeUtil
 import androidx.camera.integration.extensions.utils.ExtensionModeUtil.AVAILABLE_EXTENSION_MODES
@@ -66,12 +67,10 @@
      * Gets a list of all camera id and extension mode combinations.
      */
     @JvmStatic
-    fun getAllCameraIdExtensionModeCombinations(): List<Array<Any>> =
-        arrayListOf<Array<Any>>().apply {
-            CameraUtil.getBackwardCompatibleCameraIdListOrThrow().forEach { cameraId ->
-                ExtensionModeUtil.AVAILABLE_EXTENSION_MODES.forEach { mode ->
-                    add(arrayOf(cameraId, mode))
-                }
+    fun getAllCameraIdExtensionModeCombinations(): List<CameraIdExtensionModePair> =
+        CameraUtil.getBackwardCompatibleCameraIdListOrThrow().flatMap { cameraId ->
+            ExtensionModeUtil.AVAILABLE_EXTENSION_MODES.map { extensionMode ->
+                CameraIdExtensionModePair(cameraId, extensionMode)
             }
         }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/ImageCaptureTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/ImageCaptureTestUtil.kt
new file mode 100644
index 0000000..1875421
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/ImageCaptureTestUtil.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.util
+
+import android.graphics.BitmapFactory
+import android.graphics.ImageFormat
+import android.media.Image
+import com.google.common.truth.Truth.assertThat
+import junit.framework.TestCase.assertNotNull
+import junit.framework.TestCase.assertTrue
+
+/**
+ * Validate the image can be correctly decoded from a jpeg format to a bitmap.
+ */
+fun assertImageIsValid(image: Image, width: Int, height: Int) {
+    assertThat(image.width).isEqualTo(width)
+    assertThat(image.height).isEqualTo(height)
+    assertThat(image.format).isEqualTo(ImageFormat.JPEG)
+
+    val data = imageData(image)
+    assertTrue("Invalid image data", data.isNotEmpty())
+
+    val bmpOptions = BitmapFactory.Options().apply {
+        inJustDecodeBounds = true
+    }
+
+    BitmapFactory.decodeByteArray(data, 0, data.size, bmpOptions)
+
+    assertThat(width).isEqualTo(bmpOptions.outWidth)
+    assertThat(height).isEqualTo(bmpOptions.outHeight)
+
+    val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
+    assertNotNull("Decoding jpeg failed", bitmap)
+}
+
+private fun imageData(image: Image): ByteArray {
+    val planes = image.planes
+    assertTrue("Fail to get image planes", planes != null && planes.isNotEmpty())
+
+    val buffer = planes[0].buffer
+    assertNotNull("Fail to get jpeg ByteBuffer", buffer)
+
+    val data = ByteArray(buffer.remaining())
+    buffer.get(data)
+    buffer.rewind()
+    return data
+}
\ No newline at end of file
diff --git a/activity/activity/src/main/java/androidx/activity/Cancellable.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/CameraIdExtensionModePair.kt
similarity index 65%
copy from activity/activity/src/main/java/androidx/activity/Cancellable.java
copy to camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/CameraIdExtensionModePair.kt
index a5cb90a..629e678 100644
--- a/activity/activity/src/main/java/androidx/activity/Cancellable.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/CameraIdExtensionModePair.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.activity;
+package androidx.camera.integration.extensions.utils
 
 /**
- * Token representing a cancellable operation.
+ * Represents a pair of Camera ID and Camera Extension Mode type
  */
-interface Cancellable {
-
-    /**
-     * Cancel the subscription. This call should be idempotent, making it safe to
-     * call multiple times.
-     */
-    void cancel();
-}
+data class CameraIdExtensionModePair(val cameraId: String, val extensionMode: Int)
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
index 6f6ad1c..d269c76 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Taaklimiet is bereik\nAs jy voortgaan, sal die program verplig word om te stop"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Taak se stap %1$d van %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik om aan te beweeg"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Besoek asseblief die verskillende template en maak seker die motor is in rymodus"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstrasie van wisselknoppie"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Wissel toets"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Dinamiese veranderinge word toegelaat"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Opdaterings toegelaat vir terughandelinge."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Wisseltoets is geaktiveer"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Wisseltoets is gedeaktiveer"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekondêre handeling en versieringdemonstrasie"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Sekondêrehandelingtoets"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Net die sekondêre handeling kan gekies word"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Versieringtoets"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekondêre handelinge en versiering"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Die ry kan ook gekies word"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Sekondêre handeling is gekies"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Primêre handeling vir ry is gekies"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrasies oor diverse template"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Stal demonstrasies uit"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrasie van templaatuitlegte"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Demonstrasieskerm vir stemtoegang"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Gebruikerinteraksies"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Versoek demonstrasies van toestemmings"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Appoorvloei-bekragtiger"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Toets asseblief die volgende template terwyl jy"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"die voertuig van parkeer- na bestuurstaat toe verander"</string>
     <string name="perm_group" msgid="3834918337351876270">"Toestemminggroep"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Toestemminggroep vir uitstalprogram"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Toegang tot fyn ligging"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
index 364cd95..70ff493 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"የተግባር ገደብ ላይ ደርሷል\nወደፊት መሄድ መተግበሪያውን በኃይል ያስቆማል"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"የተግባር እርምጃ %1$d ከ%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ወደፊት ለመሄድ ጠቅ ያድርጉ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"እባክዎ የተለያዩ የቅንብር ደንቦችን ይጎብኙ እና መኪናው የማሽከርከር ሁነታ ላይ እንደሆነ ያረጋግጡ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"የቀያይር አዝራር ቅንጭብ ማሳያ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ሙከራን ቀያይር"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ተጨባጭ ለውጦች ተፈቅደዋል"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"ዝማኔዎች በዳራ ክወናዎች ላይ ይፈቀዳሉ።"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"የመቀያየር ሙከራ ነቅቷል"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"የመቀያየር ሙከራ ተሰናክሏል"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ሁለተኛ እርምጃ እና የማስዋቢያ ቅንጭብ ማሳያ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"የሁለተኛ እርምጃ ሙከራ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"መመረጥ የሚችለው ሁለተኛው እርምጃ ብቻ ነው"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"የማስዋቢያ ሙከራ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ሁለተኛ እርምጃዎች እና ማስዋብ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"እንዲሁም ረድፉ መመረጥ ይችላል"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ሁለተኛ እርምጃ ተመርጧል"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"የረድፍ አንደኛ እርምጃ ተመርጧል"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"የተለያዩ ቅንብር ደንቦች ቅንጭብ ማሳያዎች"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"የመሳያ ቅንጭብ ማሳያ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"የቅንብር ደንብ አቀማመጥ ቅንጭብ ማሳያዎች"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"የድምጽ መዳረሻ ቅንጭብ ማሳያ ማያ ገጽ"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"የተጠቃሚ መስተጋብሮች"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"የቅንጭብ ማሳያ ፈቃዶችን ይጠይቁ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"የመተግበሪያ ትርፍ አረጋጋጭ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"በሚቀይሩበት ጊዜ እባክዎ የሚከተሉትን የቅንብር ደንቦች ይሞክሩ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ተሽከርካሪው ከመቆም ወደ የማሽከርከር ሁኔታ"</string>
     <string name="perm_group" msgid="3834918337351876270">"የፈቃድ ቡድን"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ለማሳያ መተግበሪያ የፈቃድ ቡድን"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"የትክክለኛ አካባቢ መዳረሻ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
index 576646c..afb470f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"تم الوصول إلى حد المهمّة.\nسيؤدي التقدم إلى فرض إيقاف التطبيق."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"‏خطوة المهمة: %1$d من %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"انقر للتقدم إلى الأمام."</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"يُرجى الانتقال إلى النماذج المختلفة والتأكّد من أنّ السيارة في وضع القيادة."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"عرض توضيحي لزر الإيقاف/التفعيل"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"إيقاف/تفعيل الاختبار"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"يُسمَح بتغيير الحالة."</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"التحديثات المسموح بها على العمليات التي تتم بالخلفية"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"تم تفعيل زر إيقاف/تفعيل الاختبار."</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"تم إيقاف زر إيقاف/تفعيل الاختبار."</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"عرض توضيحي للإجراء الثانوي والتصميم"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"إجراء ثانوي تجريبي"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"يمكن اختيار الإجراء الثانوي فقط."</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"تصميم تجريبي"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"الإجراءات الثانوية والتصميم"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"يمكن أيضًا اختيار الصف."</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"تم اختيار إجراء ثانوي للصف."</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"تم اختيار إجراء أساسي للصف."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"إصدارات تجريبية لنموذج ميزات متنوّعة"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"عرض الإصدارات التجريبية"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"عروض توضيحية لتنسيقات النماذج"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"شاشة العرض التوضيحي للاستخدام عبر الصوت"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"تفاعلات المستخدمين"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"عروض توضيحية لطلب الأذونات"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"أداة السماح بالتجاوز"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"يُرجى تجربة النماذج التالية أثناء تغيير"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"حالة المركبة من \"مركونة\" إلى \"القيادة\""</string>
     <string name="perm_group" msgid="3834918337351876270">"مجموعة أذونات"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"مجموعة أذونات لتطبيق العرض"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"الوصول إلى الموقع الدقيق"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
index 5e1c2d3..7e4e8da 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"কাৰ্যৰ সীমাত উপনীত হৈছে\nআগবাঢ়ি গ’লে এপ্‌টো বলপূৰ্বকভাৱে বন্ধ কৰা হ’ব"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"কাৰ্যৰ পদক্ষেপ %2$d টাৰ %1$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"আগবাঢ়ি যাবলৈ ক্লিক কৰক"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"অনুগ্ৰহ কৰি বিভিন্ন টেমপ্লে’টসমূহ চাওক আৰু গাড়ীখন ড্ৰাইভিং ম’ডত থকাটো নিশ্চিত কৰক"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ট’গল বুটামৰ ডেম’"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"পৰীক্ষা ট’গল কৰক"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ষ্টে’টফুল সলনি কৰাৰ অনুমতি আছে"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"নেপথ্যৰ কাৰ্যত আপডে’টৰ অনুমতি আছে।"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"পৰীক্ষা ট’গল কৰাৰ সুবিধা সক্ষম কৰা হৈছে"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"পৰীক্ষা ট’গল কৰাৰ সুবিধা অক্ষম কৰা হৈছে"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"গৌণ কাৰ্য আৰু সজোৱা কাৰ্যৰ ডেম’"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"গৌণ কাৰ্যৰ পৰীক্ষা"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"কেৱল গৌণ কাৰ্যটোও বাছনি কৰিব পৰা যায়"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"সজোৱা কাৰ্য"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"গৌণ কাৰ্য আৰু সজোৱা কাৰ্য"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"শাৰীটোও বাছনি কৰিব পৰা যায়"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"গৌণ কাৰ্য বাছনি কৰা হৈছে"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"শাৰীৰ প্ৰাথমিক কাৰ্য বাছনি কৰা হৈছে"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"সানমিহলি টেম্পলে’টৰ ডেম’"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ডেম’ দেখুৱাওক"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"টেমপ্লে’ট লে’আউটৰ ডেম’"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"কণ্ঠধ্বনিৰে এক্সেছ কৰাৰ ডেম’ৰ স্ক্ৰীন"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ব্যৱহাৰকাৰীৰ ভাব-বিনিময়"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"অনুমতিৰ অনুৰোধৰ ডেম’"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"এপ্লিকেশ্বন অভাৰফ্ল’ৰ মান্যতা প্ৰদানকাৰী"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"অনুগ্ৰহ কৰি, সলনি কৰাৰ সময়ত তলত দিয়া টেমপ্লে’টসমূহ পৰীক্ষা কৰি চাওক"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ৰখাই থোৱা অৱস্থাৰ পৰা চলাই থকা অৱস্থালৈ বাহনখন"</string>
     <string name="perm_group" msgid="3834918337351876270">"অনুমতিৰ গোট"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase এপক দিয়া অনুমতিৰ গোট"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"সঠিক অৱস্থানৰ এক্সেছ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
index f7eab61..de50258 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Tapşırıq limitinə çatdınız\nİrəli getmək tətbiqi məcburi dayandıracaq"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Tapşırıq mərhələsi %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"İrəli keçmək üçün klikləyin"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Müxtəlif şablonları ziyarət edin və avtomobilin sürücülük rejimində olduğuna əmin olun"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Keçirmə Düyməsi Demosu"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Keçirmə testi"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Status dəyişikliyinə icazə verilir"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Geri əməliyyatlar üzrə güncəlləmələrə icazə verilir."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Keçirmə Testi Aktivdir"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Keçirmə Testi Deaktivdir"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"İkinci Dərəcəli Əməliyyat və Dekorasiya Demosu"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"İkinci Dərəcəli Əməliyyat Testi"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Yalnız ikinci dərəcəli əməliyyat seçilə bilər"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekorasiya Testi"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"İkinci Dərəcəli Əməliyyatlar və Dekorasiya"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Sıra da seçilə bilər"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"İkinci Dərəcəli Əməliyyat seçilib"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Sıra üzrə əsas əməliyyat seçilib"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Digər Şablon Demoları"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Vitrin Demoları"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Şablon Düzən Demoları"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Səsli Giriş Demo Ekranı"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"İstifadəçi əlaqələri"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"İcazə Demosu Tələb edin"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Tətbiq Aşma Doğrulayıcısı"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Avtomobili park vəziyyətindən sürmə vəziyyətinə"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"dəyişdirərkən aşağıdakı şablonları sınayın"</string>
     <string name="perm_group" msgid="3834918337351876270">"İcazə Qrupu"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Xəbərlər Vitrini Tətbiqi üçün İcazə Qrupu"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Dəqiq Məkana Giriş"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
index 21b1418..b6e3dac 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dostignuto je ograničenje zadataka\nAko nastavite, aplikacija će se prinudno zaustaviti"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d. korak zadatka od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da biste išli napred"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Posetite različite šablone i uverite se da je automobil u režimu vožnje"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstracija dugmeta za uključivanje/isključivanje"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Uključi/isključi test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Promene stanja su dozvoljene"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Ažuriranja su dozvoljena za operacije u pozadini."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test uključivanja/isključivanja je omogućen"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test uključivanja/isključivanja je onemogućen"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstracija sekundarne radnje i dekoracije"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundarne radnje"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Može da se izabere samo sekundarna radnja"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test dekoracije"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundarne radnje i dekoracija"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Može da se izabere i red"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundarna radnja je izabrana"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Primarna radnja reda je izabrana"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstracije različitih šablona"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstracije prikazivanja"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstracije izgleda šablona"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ekran demonstracije pristupa glasu"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Korisničke interakcije"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstracije zahteva za dozvole"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validator prekoračenja aplikacije"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testirajte sledeće šablone tokom punjenja"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"status vozila iz parkiranog u vožnja"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupa dozvola"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupa dozvola za isticanje aplikacije"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Pristup preciznoj lokaciji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
index 2deca03..eb81f87 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Дасягнута максімальная колькасць задач\nДалейшы рух прывядзе да прымусовага спынення праграмы"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Крок задачы %1$d з %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Націсніце, каб рухацца далей"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Праглядзіце розныя шаблоны і пераканайцеся, што аўтамабіль знаходзіцца ў рэжыме \"За рулём\""</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Дэманстрацыя кнопкі пераключэння"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Праверка пераключэння"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Дазволена змяняць стан"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Абнаўленні дазволены для аперацый вяртання."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Праверка пераключэння ўключана"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Праверка пераключэння адключана"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Дэмаверсія другаснага дзеяння і аздаблення"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тэст другаснага дзеяння"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Можна выбраць толькі другаснае дзеянне"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Тэст элементаў аздаблення"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Другасныя дзеянні і аздабленне"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Таксама можна выбраць радок"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Выбрана другаснае дзеянне"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Выбрана галоўнае дзеянне для радка"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Дэманстрацыі розных шаблонаў"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Дэманстрацыі выбранага"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Дэманстрацыі макета шаблона"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Экран дэманстрацыі Галасавога доступу"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Узаемадзеянне з карыстальнікамі"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Запытаць дэманстрацыі дазволаў"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Сродак праверкі дадатковага меню праграмы"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Пры змяненні пратэсціруйце наступныя шаблоны"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"змяненне стану аўтамабіля з \"Прыпаркаваны\" на \"За рулём\""</string>
     <string name="perm_group" msgid="3834918337351876270">"Група дазволаў"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Група дазволаў для праграмы \"Выбранае\""</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Доступ да дакладнага месцазнаходжання"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
index 2aad28d..f278d69 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Ограничението за задачи е достигнато\nАко продължите, приложението ще бъде спряно принудително"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Стъпка %1$d от %2$d в задачата"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Кликнете, за да продължите напред"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Отворете отделните шаблони, докато автомобилът е в режим за шофиране"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Демонстрация на бутон за превключване"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Тест с превключвател"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Промените на състоянието са разрешени"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Актуализациите са разрешени за операции за връщане назад."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Тестът с превключвател е активиран"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Тестът с превключвател е деактивиран"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрация на алтернативно действие и декоративен елемент"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тестване на алтернативно действие"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Само алтернативното действие може да бъде избрано"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Тестване на декоративен елемент"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Алтернативни действия и декоративни елементи"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Редът може също да бъде избран"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Избрано е алтернативното действие"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Избрано е основното действие за реда"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрации на разни шаблони"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрации на Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрации на оформления за шаблон"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Екран за демонстрация на Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Потребителски взаимодействия"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрации за заявяване на разрешения"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Проверка при препълване за приложението"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Тествайте следните шаблони, докато превключвате между"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"режим за паркиране и шофиране"</string>
     <string name="perm_group" msgid="3834918337351876270">"Група разрешения"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Група разрешения за приложението „На фокус“"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Достъп до точното местоположение"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
index 541d5e3..282632f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"টাস্কের সীমা পেরিয়ে গেছে\nআরও এগোলে, অ্যাপকে জোর করে বন্ধ করা হবে"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"টাস্কের %2$d ধাপের %1$d নম্বর ধাপ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"এগিয়ে যাওয়ার জন্য ক্লিক করুন"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"বিভিন্ন টেমপ্লেট দেখুন ও নিশ্চিত করুন যে গাড়িটি \'ড্রাইভিং\' মোডে আছে"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"টগল বোতাম সংক্রান্ত ডেমো"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"টগল টেস্ট"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"স্টেটফুল পরিবর্তন করার অনুমতি দেওয়া হয়েছে"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"পিছনের পৃষ্ঠাতে গিয়ে পরিবর্তন করা যাবে।"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"টগল টেস্ট চালু করা হয়েছে"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"টগল টেস্ট বন্ধ করা হয়েছে"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"সেকেন্ডারি অ্যাকশন ও ডেকরেশন ডেমো"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"সেকেন্ডারি অ্যাকশন টেস্ট"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"শুধুমাত্র সেকেন্ডারি অ্যাকশন বেছে নেওয়া যেতে পারে"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ডেকরেশন টেস্ট"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"সেকেন্ডারি অ্যাকশন ও ডেকরেশন"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"সারিও বেছে নেওয়া যেতে পারে"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"সেকেন্ডারি অ্যাকশন বেছে নেওয়া হয়েছে"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"সারির প্রাইমারি অ্যাকশন বেছে নেওয়া হয়েছে"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"অন্যান্য টেমপ্লেটের ডেমো"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ডেমো শোকেস করুন"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"টেমপ্লেট লেআউট সংক্রান্ত ডেমো"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"\'ভয়েস অ্যাক্সেস\' অ্যাপের ডেমো স্ক্রিন"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ব্যবহারকারীর ইন্টার‍্যাকশন"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"অনুমতি সংক্রান্ত ডেমোর জন্য অনুরোধ জানানো"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"অ্যাপ্লিকেশন ওভারফ্লো ভ্যালিডেটর"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"পরিবর্তন করার সময় নিম্নলিখিত টেমপ্লেট পরীক্ষা করুন"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"গাড়ি পার্ক করা থেকে ড্রাইভিং অবস্থা"</string>
     <string name="perm_group" msgid="3834918337351876270">"অনুমতি সংক্রান্ত গ্রুপ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase অ্যাপের জন্য অনুমতি সংক্রান্ত গ্রুপ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"সুনির্দিষ্ট লোকেশনে অ্যাক্সেস"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
index f92b5a5..e2b505e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dostignuto je ograničenje za zadatak\nAko nastavite, prisilno ćete zaustaviti aplikaciju"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Korak zadatka: %1$d od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da idete naprijed"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Posjetite različite šablone i pobrinite se da automobil bude u načinu rada za vožnju"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo verzija dugmeta za uključivanje/isključivanje"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Testiranje uključivanja/isključivanja"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Dozvoljene su izmjene koje zavise od statusa"</string>
@@ -307,14 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Dozvoljena su ažuriranja u vezi s radnjama navigiranja prema nazad."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Testiranje uključivanja/isključivanja je omogućeno"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Testiranje uključivanja/isključivanja je onemogućeno"</string>
-    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Pokazna verzija sekundarne radnje i dekoracije"</string>
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekundarna radnja i demo verzija dekoracije"</string>
     <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundarne radnje"</string>
-    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Može se odabrati samo sekundarna radnja"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Moguće je odabrati samo sekundarnu radnju"</string>
     <string name="decoration_test_title" msgid="8450127046762442244">"Test dekoracije"</string>
     <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundarne radnje i dekoracija"</string>
-    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Može se odabrati i redak"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Moguće je odabrati i red"</string>
     <string name="secondary_action_toast" msgid="5076434693504006565">"Odabrana je sekundarna radnja"</string>
-    <string name="row_primary_action_toast" msgid="756516694751965204">"Odabran je redak primarne radnje"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Odabrana je primarna radnja reda"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo verzije raznih šablona"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo verzije predstavljanja"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo verzije rasporeda predloška"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ekran demo verzije Pristupa glasom"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Korisničke interakcije"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Zatraži demo verzije odobrenja"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Alat za provjeru preklopnog menija aplikacije"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testirajte sljedeće šablone tokom promjene"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"vozilo iz stanja parkiranosti u stanje vožnje"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupa odobrenja"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupa odobrenja za aplikaciju Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Pristup preciznoj lokaciji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
index db8e8fa..e51c494 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"S\'ha arribat al límit de tasques\nEn continuar es forçarà l\'aturada de l\'aplicació"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Pas de la tasca %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Fes clic per anar endavant"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Visita les diferents plantilles i comprova que el cotxe sigui en mode de conducció"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demostració d\'un botó de commutació"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test de commutació"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Es permeten canvis amb estat"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Es permeten actualitzacions a les operacions d\'anar enrere."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Prova de commutació activada"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Prova de commutació desactivada"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demostració de l\'acció secundària i la decoració"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prova de l\'acció secundària"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Només es pot seleccionar l\'acció secundària"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Prova de decoració"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Accions secundàries i decoració"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"La fila també es pot seleccionar"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"S\'ha seleccionat l\'acció secundària"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"S\'ha seleccionat l\'acció principal de la fila"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demostracions de plantilles diverses"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demostracions de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demostracions de dissenys de plantilles"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de demostració de Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaccions dels usuaris"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demostracions de sol·licitud de permisos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador del menú addicional de l\'aplicació"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prova les següents plantilles mentre canvies"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehicle de l\'estat aparcat a mode de conducció"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grup de permisos"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grup de permisos de l\'aplicació Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Accés a la ubicació precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
index ba7bab0..5f4d0ad 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Byl dosažen limit úkolů\nPokračováním vynutíte ukončení aplikace"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Krok úkolu %1$d z %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknutím přejdete vpřed"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Navštivte různé šablony a ujistěte se, že je auto v režimu jízdy"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Ukázka přepínače"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test přepínače"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Sledované změny jsou povoleny"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"U zpětných operací jsou povoleny aktualizace."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test přepínače je povolen"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test přepínače byl zakázán"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Ukázka sekundární akce a dekorace"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundární akce"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Lze vybrat pouze sekundární akci"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Zkouška dekorace"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundární akce a dekorace"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Řádek lze také vybrat"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Je vybrána sekundární akce"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Je vybrána primární akce řádku"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Ukázky různých šablon"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Ukázky Výběru"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Ukázky šablon rozvržení"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ukázka obrazovky pro ovládání hlasem"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interakce uživatelů"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Ukázky žádostí o oprávnění"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validátor přetečení aplikace"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Vyzkoušejte následující šablony při přechodu"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"auta ze zaparkovaného do jedoucího stavu"</string>
     <string name="perm_group" msgid="3834918337351876270">"Skupina oprávnění"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Skupina oprávnění pro vystavenou aplikaci"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Přístup k přesné poloze"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
index 03958d9..5039e9d 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Grænsen for opgaver blev nået\nHvis du fortsætter, tvinges appen til at standse"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Opgavetrin %1$d af %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik for at gå videre"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Gå til de forskellige skabeloner, og tjek, at bilen er i køretilstand"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo for kontakt"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Slå test til/fra"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Tilstandsfulde ændringer er tilladt"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Demoskærm for Stemmeadgang"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Brugerinteraktioner"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Anmod om tilladelsesdemoer"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validator for overløb af apps"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Test følgende skabeloner, mens du skifter"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"bilens tilstand fra Parkeret til Kører"</string>
     <string name="perm_group" msgid="3834918337351876270">"Tilladelsesgruppe"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Tilladelsesgruppe for Showcase-appen"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Adgang til nøjagtig lokation"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
index e3c79af..0ea9b40 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Aufgabenlimit erreicht\nDurch Fortfahren wird das Beenden der App erzwungen"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Aufgabe: Schritt %1$d von %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klicken, um fortzufahren"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Bitte rufe die verschiedenen Vorlagen auf und achte darauf, dass sich das Auto im Fahrmodus befindet"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo der Ein-/Aus-Schaltfläche"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test der Ein-/Aus-Schaltfläche"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Zustandsorientierte Änderungen sind erlaubt"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Updates beim Zurückgehen erlaubt."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test der Ein-/Aus-Schaltfläche aktiviert"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test der Ein-/Aus-Schaltfläche deaktiviert"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo der sekundären Aktion und der Dekoration"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test für sekundäre Aktion"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Nur die sekundäre Aktion kann ausgewählt werden"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekorationstest"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundäre Aktionen und Dekoration"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Auch diese Zeile kann ausgewählt werden"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundäre Aktion ist ausgewählt"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Zeile mit der primären Aktion ist ausgewählt"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demos der verschiedenen Vorlagen"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demos anzeigen"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo der Layoutvorlagen"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Bildschirm für Voice Access-Demo"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Nutzerinteraktionen"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demos zur Berechtigungsanfrage"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Anwendungs-Overflow-Validator"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Bitte teste die folgenden Vorlagen, wenn du"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"das Fahrzeug vom Park- in den Fahrmodus umschaltest"</string>
     <string name="perm_group" msgid="3834918337351876270">"Berechtigungsgruppe"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Berechtigungsgruppe für die Showcase App"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Zugriff auf genauen Standort"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
index 5213362..13c3309 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Συμπληρώθηκε το όριο εργασιών\nΜε μετάβαση προς τα εμπρός, θα γίνει αναγκαστική διακοπή της εφαρμογής."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Βήμα εργασίας %1$d από %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Κάντε κλικ για μετάβαση εμπρός"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Επισκεφτείτε διαφορετικά πρότυπα και διασφαλίστε ότι το αυτοκίνητό σας είναι σε λειτουργία οδήγησης"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Επίδειξη κουμπιού εναλλαγής"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Εναλλαγή δοκιμής"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Επιτρέπονται οι αλλαγές με επίβλεψη κατάστασης"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Οθόνη επίδειξης Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Αλληλεπιδράσεις χρήστη"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Αίτημα για επιδείξεις αδειών"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Επικύρωση υπερχείλισης εφαρμογής"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Δοκιμάστε τα παρακάτω πρότυπα κατά την αλλαγή"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"της κατάστασης του αυτοκινήτου από στάθμευση σε οδήγηση"</string>
     <string name="perm_group" msgid="3834918337351876270">"Ομάδα αδειών"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Ομάδα αδειών για την εφαρμογή Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Πρόσβαση στην Ακριβή τοποθεσία"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
     <string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
     <string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
     <string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
     <string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
index 1e8bca3..dabdb75 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Se alcanzó el límite de tareas\nSi avanzas, se forzará la detención de la app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d de la tarea"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Haz clic para avanzar"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Visita las diferentes plantillas y asegúrate de que el automóvil esté en modo de conducción"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demostración de botón de activación"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Prueba el botón de activación"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Se permiten los cambios con estado"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Se permiten las actualizaciones en las operaciones de retroceso."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Prueba del botón de activación habilitada"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Prueba del botón de activación desactivada"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demostración de una acción secundaria y decoración"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prueba de la acción secundaria"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Sólo puede seleccionarse la acción secundaria"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Prueba de decoración"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Acciones secundarias y decoraciones"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"También se puede seleccionar la fila"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Se seleccionó una acción secundaria"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Se seleccionó una acción de fila principal"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demostraciones de plantillas varias"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demostraciones de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demostración de plantilla de diseño"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de demostración de Acceso por voz"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interacciones del usuario"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demostración de solicitudes de permisos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador de exceso de aplicaciones"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prueba las siguientes plantillas mientras cambias"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehículo del estado de estacionamiento al de conducción"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupo de permisos"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupo de permisos para la app de Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Acceso a la ubicación precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
index 3db6907..1d82de7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Se ha alcanzado el límite de tareas\nSi continuas, se forzará la detención de la aplicación"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d de la tarea"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Haz clic para continuar"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Consulta las diferentes plantillas y comprueba que el coche esté en modo de conducción"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo de botón activar/desactivar"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Prueba del interruptor"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Se permiten cambios de estado"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Actualizaciones permitidas al ir atrás."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Prueba del interruptor habilitada"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Prueba del interruptor inhabilitada"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo sobre acción secundaria y decoración"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prueba de acción secundaria"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Solo se puede seleccionar la acción secundaria"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Prueba de decoración"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Acciones secundarias y decoración"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"También se puede seleccionar la fila"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Se ha seleccionado la acción secundaria"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Se ha seleccionado la acción principal de la fila"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Otras demos de plantillas"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demos de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demos de diseño de plantillas"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de la demo de Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interacciones de usuarios"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demos de solicitud de permisos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador de desbordamiento de aplicaciones"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prueba las siguientes plantillas mientras se hace el cambio"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehículo del estado de estacionamiento al de conducción"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupo de permisos"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupo de permisos de la aplicación Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Acceso a la ubicación precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
index 40d2630..9d508d5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Ülesannete piirang on saavutatud\nJätkamisel rakendus sundsuletakse"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Ülesande toiming %1$d %2$d-st"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klõpsake edasiliikumiseks"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Vaadake eri mallid üle ja veenduge, et auto oleks sõidurežiimis"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Lülitusnupu demo"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Testi lüliti"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Olekulised muudatused on lubatud"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Värskendused on tagasiminekutoimingute korral lubatud."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Lülititest on lubatud"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Lülititest on keelatud"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Teisese toimingu ja dekoratsiooni demo"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Teisese toimingu test"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Valida saab ainult teisese toimingu"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekoratsiooni test"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Teisesed toimingud ja dekoratsioon"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Rea saab samuti valida"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Valitud on teisene toiming"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Valitud on rea peamine toiming"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Mitmesuguste mallide demod"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Esiletõstmise demod"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Malli paigutuse demod"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Accessi demo kuva"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Kasutaja interaktsioonid"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Lubade taotlemise demod"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Rakenduse ületäite kinnitaja"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testige järgmisi malle, kui lülitate"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"sõidukit pargitud olekust sõitmise olekusse"</string>
     <string name="perm_group" msgid="3834918337351876270">"Lubade grupp"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase\'i rakenduse lubade grupp"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Juurdepääs täpsele asukohale"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
index 447e227..eda6e05 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Zereginen mugara iritsi zara\nAurrera eginez gero, aplikazioa gelditzera behartuko duzu"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Zereginaren %1$d/%2$d urratsa"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Aurrera egiteko, sakatu hau"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Ikusi txantiloiak eta ziurtatu ibilgailua gidatze moduan dagoela"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Etengailuaren demo-bertsioa"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Etengailuaren proba"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Aldaketa egoeradunak egin daitezke"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Atzeko planoko eragiketak eguneratu egin daitezke."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Gaitu da etengailuaren proba"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Desgaitu da etengailuaren proba"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Bigarren mailako ekintzaren eta apainketaren demo-bertsioa"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Bigarren mailako ekintzaren proba"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Bigarren mailako ekintza soilik hauta daiteke"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Apainketaren proba"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Bigarren mailako ekintzak eta apainketa"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Errenkada ere hauta daiteke"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Bigarren mailako ekintza hautatuta dago"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Errenkadari dagokion ekintza nagusia hautatuta dago"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Bestelako txantiloien demo-bertsioak"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Erakutsi demo-bertsioak"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Txantiloi-diseinuen demo-bertsioak"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ahots bidezko sarbidearen demo-bertsioaren pantaila"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Erabiltzailearen interakzioak"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Baimen-eskaeren demo-bertsioak"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Aplikazio-luzapenen baliozkotzailea"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Egin txantiloi hauen probak"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ibilgailua \"aparkatuta\" egoeratik \"gidatzen\" egoerara aldatu bitartean"</string>
     <string name="perm_group" msgid="3834918337351876270">"Baimenen taldea"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase aplikazioaren baimenen taldea"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Kokapen zehatzerako sarbidea"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
index 4dbd909..1cd0cda 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"به حد مجاز تکلیف رسیدید\nاگر ادامه دهید، برنامه به‌اجبار متوقف خواهد شد"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"‏مرحله %1$d از %2$d تکلیف"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"برای ادامه دادن، کلیک کنید"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"لطفاً از الگوهای مختلف بازدید کنید و مطمئن شوید خودرو در حالت رانندگی باشد"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"نسخه نمونه دکمه مبدل"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"امتحان کردن تغییر وضعیت"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"تغییرات حالت‌مند مجاز است"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"به‌روزرسانی در عملیات‌های برگشتی مجاز است."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"آزمایش تغییر وضعیت فعال شد"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"آزمایش تغییر وضعیت غیرفعال شد"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"نمونه آرایه‌گری و کنش فرعی"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"آزمایش کنش فرعی"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"فقط کنش فرعی را می‌توان انتخاب کرد"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"آزمایش آرایه‌گری"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"کنش‌های فرعی و آرایه‌گری"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ردیف را هم می‌توان انتخاب کرد"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"کنش فرعی انتخاب شده است"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"کنش اصلی ردیف انتخاب شده است"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"الگوهای متفرقه نمونه"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"نمایش نمونه‌ها"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"نسخه‌های نمونه طرح‌بندی الگو"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"صفحه نمونه دسترسی صوتی"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"تعاملات کاربران"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"درخواست اجازه برای نسخه‌های نمونه"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"اعتبارسنج سرریز برنامه"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"لطفاً درحین تغییر دادن، الگوهای زیر را آزمایش کنید"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"وسیله نقلیه از حالت پارک به حالت رانندگی"</string>
     <string name="perm_group" msgid="3834918337351876270">"گروه اجازه"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"گروه اجازه برنامه «ویترین»"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"دسترسی به مکان دقیق"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
index 622bd9e..f5de8d0 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Tehtäväraja saavutettu\nEteneminen pakottaa sovelluksen sulkeutumaan"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Tehtävän vaihe %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Siirry eteenpäin klikkaamalla"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Tarkista eri mallit ja varmista, että auto on ajotilassa"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Päälle/pois-painikkeen esittely"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Vaihtotesti"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Tilattomat muutokset ovat sallittuja"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Päivitykset sallittu siirryttäessä edelliseen näkymään."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Vaihtotesti käytössä"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Vaihtotesti pois käytöstä"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Toissijainen toiminto ja kuvio ‑demo"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Toissijaisen toiminnon testi"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Voit valita vain toissijaisen toiminnon"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Kuviotesti"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Toissijaiset toiminnot ja kuvio"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Voit valita myös rivin"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Toissijainen toiminto on valittu"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Rivin ensisijainen toiminto on valittu"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Sekalaisten mallien esittelyt"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase-esittelyt"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Malliasettelujen esittelyt"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Accessin esittelynäyttö"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Käyttäjän toiminta"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Pyydä lupien esittelyjä"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Sovelluksen ylivuodon tarkistustyökalu"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testaa seuraavat mallit vaihtaessasi"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"pysäköidystä autosta ajettuun autoon"</string>
     <string name="perm_group" msgid="3834918337351876270">"Luparyhmä"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Esittely-sovelluksen luparyhmä"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Pääsy tarkkaan sijaintiin"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
index a215254..1f27a42 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite de la tâche atteinte\nAller de l\'avant forcera l\'arrêt de l\'application"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Étape %1$d de %2$d de la tâche"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Cliquez pour avancer"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Veuillez consulter les différents modèles et vous assurer que la voiture est en mode Voiture"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Démo du commutateur"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Essai du commutateur"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Les changements à état sont autorisés"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Mises à jour autorisées lors des opérations de retour."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Essai du commutateur activé"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Essai du commutateur désactivé"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Démo d\'action secondaire et de décoration"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test d\'action secondaire"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Seule l\'action secondaire peut être sélectionnée"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test de décoration"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Actions secondaires et décoration"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Cette ligne peut également être sélectionnée"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"L\'action secondaire est sélectionnée"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"L\'action primaire de la ligne est sélectionnée"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Démos de divers modèles"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Présenter les démos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Démos de mise en page du modèle"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Écran d\'autorisation à l\'accès vocal"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interactions avec l\'utilisateur"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demande d\'autorisation pour les démos."</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Outil de validation de menu à développer d\'application"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Veuillez tester les modèles suivants lorsque vous basculez"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"l\'état du véhicule stationné à celui de conduite"</string>
     <string name="perm_group" msgid="3834918337351876270">"Groupe d\'autorisations"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Groupe d\'autorisations pour l\'application Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Accès à la localisation précise"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
index b38ddf4..917226e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite de tâche atteinte\nContinuer forcera l\'arrêt de l\'appli"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Étape %1$d sur %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Cliquer pour continuer"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Veuillez consulter les différents modèles et vous assurer que le véhicule est en mode Voiture."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Démo du bouton d\'activation"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Activer/Désactiver le test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Les modifications avec état sont autorisées"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Mises à jour autorisées sur les opérations de retour."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test d\'activation/de désactivation activé"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test d\'activation/de désactivation désactivé"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Démo de l\'action secondaire et de la décoration"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test de l\'action secondaire"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Seule l\'action secondaire peut être sélectionnée"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test de la décoration"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Actions secondaires et décoration"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"La ligne peut également être sélectionnée"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"L\'action secondaire est sélectionnée"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"L\'action principale de la ligne est sélectionnée"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Démos de divers modèles"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Présenter les démos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Démos de mise en page du modèle"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Écran de démo Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interactions des utilisateurs"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Démos de demandes d\'autorisation"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Programme de validation du menu de développement de l\'appli"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Veuillez tester les modèles suivant lorsque vous faites passer"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"l\'état du véhicule de garé à en mouvement"</string>
     <string name="perm_group" msgid="3834918337351876270">"Groupe d\'autorisations"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Groupe d\'autorisations pour l\'appli Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Accès à la position précise"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
index 7610451..f8d344e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
@@ -307,22 +307,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Permítense actualizacións ao ir á pantalla anterior."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Activouse a proba do interruptor"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Desactivouse a proba do interruptor"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Acción secundaria e demostración de decoración"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Proba da acción secundaria"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Só se pode seleccionar a acción secundaria"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Proba de decoración"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Accións secundarias e decoración"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Tamén se pode seleccionar a fila"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Seleccionouse a acción secundaria"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Seleccionouse a acción principal da fila"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Outras demostracións de modelos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demostracións de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demostracións de deseños de modelo"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
index c64878f..fc2bc84 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"કાર્યની મર્યાદાએ પહોંચી ગયા\nઆગળ વધવાથી ઍપ ફરજિયાત બંધ થઈ જશે"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$dમાંથી %1$d કાર્ય માટેનું પગલું"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"આગળ વધવા માટે ક્લિક કરો"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"કૃપા કરીને વિભિન્ન નમૂનાઓની મુલાકાત લો અને ખાતરી કરો કે કાર ડ્રાઇવિંગ મોડમાં છે"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ટૉગલ બટનનો ડેમો"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"પરીક્ષણ ટૉગલ કરો"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"સ્ટેટસમાં થતા ફેરફારનો મંજૂરી છે"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"અગાઉના કાર્યો પર અપડેટ કરવાની મંજૂરી છે."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"ટૉગલનું પરીક્ષણ ચાલુ કર્યું"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"ટૉગલનું પરીક્ષણ બંધ કર્યું"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ગૌણ ઍક્શન અને ડેકોરેશન ડેમો"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ગૌણ ઍક્શન પરીક્ષણ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"માત્ર ગૌણ ઍક્શન પસંદ કરી શકાય છે"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ડેકોરેશન પરીક્ષણ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ગૌણ ઍક્શન અને ડેકોરેશન"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"પંક્તિ પણ પસંદ કરી શકાય છે"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ગૌણ ઍક્શન પસંદ કરી"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"પ્રાથમિક ઍક્શન પંક્તિ પસંદ કરી"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"વિવિધ નમૂનાઓના ડેમો"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ડેમો બતાવો"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"નમૂનાના લેઆઉટનો ડેમો"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Accessના ડેમોની સ્ક્રીન"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"વપરાશકર્તાની ક્રિયાપ્રતિક્રિયાઓ"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"પરવાનગીઓના ડેમોની વિનંતી કરો"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ઍપ્લિકેશન ઓવરફ્લો વૅલિડેટર"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"વાહનને પાર્ક કરેલામાંથી ડ્રાઇવિંગ સ્થિતિમાં બદલતી વખતે"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"કૃપા કરીને નીચે આપેલા નમૂનાનું પરીક્ષણ કરો"</string>
     <string name="perm_group" msgid="3834918337351876270">"પરવાનગીનું ગ્રૂપ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase ઍપ માટે પરવાનગીનું ગ્રૂપ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"સચોટ લોકેશનનો ઍક્સેસ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
index 3d7f0af..44b1b65 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"टास्क की सीमा पूरी हुई\nआगे जाने पर, ऐप्लिकेशन को ज़बरदस्ती रोक दिया जाएगा"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"टास्क के %2$d चरणों में से %1$d चरण"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"आगे जाने के लिए क्लिक करें"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"कृपया अलग-अलग टेंप्लेट पर जाकर, यह पक्का करें कि कार ड्राइविंग मोड में है"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"टॉगल बटन का डेमो"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"टॉगल टेस्ट"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"स्टेटफ़ुल बदलाव करने की अनुमति है"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"पिछले पेज पर जाकर बदलाव किए जा सकते हैं."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"टॉगल टेस्ट चालू किया गया"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"टॉगल टेस्ट बंद किया गया"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"सेकंडरी ऐक्शन और डेकोरेशन का डेमो"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"सेकंडरी ऐक्शन टेस्ट"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"सिर्फ़ सेकंडरी ऐक्शन को चुना जा सकता है"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"डेकोरेशन टेस्ट"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"सेकंडरी ऐक्शन और डेकोरेशन"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"पंक्ति को भी चुना जा सकता है"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"सेकंडरी ऐक्शन चुना गया"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"पंक्ति से जुड़ा प्राइमरी ऐक्शन चुना गया"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"दूसरे टेंप्लेट के डेमो"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"डेमो दिखाएं"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"टेंप्लेट लेआउट के डेमो"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access के डेमो की स्क्रीन"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"यूज़र इंटरैक्शन"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"अनुमतियों के अनुरोध का डेमो"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ऐप्लिकेशन ओवरफ़्लो वैलिडेटर"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"कार की स्थिति को पार्क से ड्राइविंग में बदलते समय"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"दिए गए टेंप्लेट आज़माएं"</string>
     <string name="perm_group" msgid="3834918337351876270">"अनुमतियों का ग्रुप"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase ऐप्लिकेशन को दी गई अनुमतियों का ग्रुप"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"जगह की सटीक जानकारी का ऐक्सेस"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
index 7b12f9d..dbe9520 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dosegnuto ograničenje zadatka\nAko nastavite, aplikacija će se prisilno zaustaviti"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Korak zadatka: %1$d od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da biste nastavili"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Posjetite različite predloške i potvrdite da je automobil u načinu za vožnju"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Pokazna verzija prekidača"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Prebaci test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Dopuštene su promjene u praćenju stanja"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Pokazna verzija zaslona za Glasovni pristup"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Korisničke interakcije"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Zatraži pokazne verzije dopuštenja"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validator prelijevanja aplikacije"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testirajte sljedeće predloške tijekom promjene"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"stanja vozila iz parkiranog u vožnju"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupa dopuštenja"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupa dopuštenja za aplikaciju Odabir urednika"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Pristup preciznoj lokaciji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
index cd2c9c8..919d60a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Feladatkorlát elérve\nA továbblépés az app bezárását kényszeríti"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d/%1$d. lépés a feladatban"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kattintson a továbblépéshez"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Keresse fel a különböző sablonokat, és ellenőrizze, hogy az autó vezetési módban van-e"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Váltógomb bemutatója"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Kapcsolóteszt"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Az állapotmegőrző módosítások engedélyezettek"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Frissítések engedélyezve a visszalépési műveleteknél."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Kapcsolóteszt engedélyezve"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Kapcsolóteszt letiltva"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Másodlagos művelet és dekorációs demó"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Másodlagos művelet tesztelése"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Csak a másodlagos művelet választható ki"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekoráció tesztelése"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Másodlagos műveletek és dekorációk"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"A sor is kiválasztható"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Másodlagos művelet kiválasztva"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Sor elsődleges művelete kiválasztva"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Egyéb sablonok – demók"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Kirakat – demók"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Sablonelrendezések bemutatói"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Hangalapú hozzáférés képernyő – demó"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Felhasználói interakciók"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Engedélyek kérése – demók"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"További alkalmazáselemeket tartalmazó menü ellenőrzője"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Tesztelje a következő sablonokat, miközben"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"a járművel parkolásból vezetés állapotba vált"</string>
     <string name="perm_group" msgid="3834918337351876270">"Engedélycsoport"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Engedélycsoport a bemutató alkalmazáshoz"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Pontos helymeghatározáshoz való hozzáférés"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
index 3c3fc70..375942e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Առաջադրանքների սահմանաչափը սպառված է։\nԵթե շարունակեք, հավելվածի աշխատանքը կկանգնեցվի։"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Առաջադրանքի քայլ %1$d՝ %2$d-ից"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Սեղմեք՝ առաջ անցնելու համար"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Դիտեք տարբեր ձևանմուշներ և համոզվեք, որ մեքենան վարելու ռեժիմում է"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Փոխանջատիչի դեմո"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Միացնել/անջատել ստուգումը"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Կարգավիճակի փոփոխությունները թույլատրված են"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Թարմացումները թույլատրված են՝ ֆոնային ռեժիմում գործողություններ կատարելու համար։"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Փոխանջատիչի փորձարկումը միացված է"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Փոխանջատիչի փորձարկումն անջատված է"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Երկրորդական գործողության և ձևավորման դեմո"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Երկրորդական գործողության թեստ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Կարելի է ընտրել միայն երկրորդական գործողությունը"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Ձևավորման թեստ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Երկրորդական գործողություններ և ձևավորում"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Շարքը ևս կարելի է ընտրել"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Ընտրված է շարքի երկրորդական գործողությունը"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Ընտրված է շարքի գլխավոր գործողությունը"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Այլ ձևանմուշների դեմոներ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Ցուցադրել դեմոները"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Ձևանմուշի դասավորության դեմոներ"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access-ի դեմոյի էկրան"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Օգտատիրոջ փոխազդումներ"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Թույլտվության հարցումների դեմոներ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Հավելվածի լրացուցիչ ընտրացանկի ստուգում"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Փորձարկեք հետևյալ ձևանմուշները, երբ փոխում եք"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"մեքենայի ռեժիմը կայանվածից վարելու ռեժիմի"</string>
     <string name="perm_group" msgid="3834918337351876270">"Թույլտվությունների խումբ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase հավելվածի թույլտվությունների խումբ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Ճշգրիտ տեղադրության հասանելիություն"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
index c5708cf..4735ecd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Batas tugas tercapai\nJika dilanjutkan, aplikasi akan dipaksa berhenti"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Langkah tugas %1$d dari %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik untuk melanjutkan"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Buka template lain dan pastikan mobil dalam mode mengemudi"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo Tombol"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Pengujian tombol"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Perubahan stateful diizinkan"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Update diizinkan di halaman sebelumnya."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Pengujian Tombol Diaktifkan"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Pengujian Tombol Dinonaktifkan"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo Tindakan Sekunder dan Dekorasi"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Pengujian Tindakan Sekunder"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Hanya tindakan sekunder yang dapat dipilih"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Pengujian Dekorasi"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Tindakan Sekunder dan Dekorasi"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Baris juga dapat dipilih"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Tindakan Sekunder dipilih"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Tindakan utama baris dipilih"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo Template Lain-Lain"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo Berita Pilihan"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Tata Letak Template"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Layar Demo Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaksi Pengguna"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demo Permintaan Izin"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validator Overflow Aplikasi"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Uji template berikut saat mengubah"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"status kendaraan dari parkir ke mengemudi"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grup Izin"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grup Izin untuk Aplikasi Etalase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Akses ke Lokasi Terperinci"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
index e81894d..5ffd5fb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Verkefnismörkum náð\nEf haldið er áfram verður lokun forrits þvinguð"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Verkefnisskref %1$d af %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Smelltu til að halda áfram"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Opnaðu mismunandi sniðmát og gakktu úr skugga um að bíllinn sé í akstursstillingu"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Sýnishorn hnapps til að slökkva og kveikja"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Velja/afvelja prófun"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Breytingar á stöðu eru leyfðar"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Uppfærslur leyfðar á fyrri aðgerðum."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Kveikt á rofaprófun"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Slökkt á rofaprófun"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Aukaaðgerð og prufuskreyting"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prufa aukaaðgerðar"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Aðeins er hægt að velja aukaaðgerð"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Prufuskreyting"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Aukaaðgerðir og skreyting"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Einnig er hægt að velja línuna"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Aukaaðgerð er valin"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Aðalaðgerð línu er valin"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Sýnishorn ýmissa sniðmáta"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Prufuútgáfur Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Sýnishorn sniðmátsuppsetningar"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Prufuskjár fyrir raddskipanir"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Aðgerðir notenda"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Sýnishorn heimildabeiðna"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Notkunarprófun á yfirflæði forrits"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prófaðu eftirfarandi sniðmát á meðan skipt er á milli þess að hafa"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ökutækið kyrrstætt eða akandi"</string>
     <string name="perm_group" msgid="3834918337351876270">"Heimildahópur"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Heimildahópur fyrir Showcase forritið"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Aðgangur að nákvæmri staðsetningu"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
index 09c13bf..c3a7bdf 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite attività raggiunto\nSe prosegui forzerai l\'interruzione dell\'app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Passaggio attività %1$d di %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Fai clic per proseguire"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Controlla i diversi modelli e assicurati che l\'auto sia in modalità Auto"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo Pulsante di attivazione/disattivazione"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test attivazione/disattivazione"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Sono consentite modifiche stateful"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Aggiornamenti consentiti per le operazioni in background."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test di attivazione/disattivazione abilitato"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test di attivazione/disattivazione disabilitato"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo su azione secondaria e decorazione"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test azione secondaria"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Solo l\'azione secondaria può essere selezionata"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test decorazione"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Azioni secondarie e decorazione"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Anche la riga può essere selezionata"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"È stata selezionata l\'azione secondaria"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"È stata selezionata l\'azione principale della riga"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo modelli vari"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Layout modello"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Schermata demo Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interazioni dell\'utente"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demo su come richiedere le autorizzazioni"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Strumento di convalida del menu Extra dell\'applicazione"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testa i seguenti modelli mentre lo stato"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"del veicolo passa da \"Parcheggiato\" a \"Alla guida\""</string>
     <string name="perm_group" msgid="3834918337351876270">"Gruppo di autorizzazioni"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Gruppo di autorizzazioni relativo all\'app Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Accesso alla posizione precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
index 7fc4821..f5bc7e7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"הגעת להגבלת המשימות\nהמשך יוביל לסגירה ידנית של האפליקציה"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"‏שלב %1$d מתוך %2$d במשימה"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"יש ללחוץ כדי להמשיך"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"יש לבדוק את התבניות השונות ולוודא שהמכונית במצב נהיגה"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"הדגמה של מתג להחלפת מצב"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"בדיקה של החלפת המצב"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"מותר לערוך שינויי מצב"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"‏מסך ההדגמה של אפליקציית Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"אינטראקציות של משתמשים"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"הדגמה של בקשת הרשאות"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"כלי התיקוף לגבי גלישה באפליקציה"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"יש לבדוק את התבניות הבאות במהלך שינוי"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"מצב הרכב מ\'חניה\' ל\'נהיגה\'"</string>
     <string name="perm_group" msgid="3834918337351876270">"קבוצת הרשאות"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"‏קבוצת הרשאות לאפליקציית Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"גישה למיקום המדויק"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
index 772f3a6..e697085 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"タスクの上限に達しました\n続行するとアプリが強制停止されます"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"タスクのステップ %1$d / %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"クリックして続行"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"別のテンプレートにアクセスして自動車が運転モードであることを確認してください"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"切り替えボタンのデモ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"切り替えのテスト"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ステートフルな変更が許可されています"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"アップデートにより「戻る」操作ができるようになりました。"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"切り替えのテスト有効"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"切り替えのテスト無効"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"セカンダリ アクションとデコレーションのデモ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"セカンダリ アクション テスト"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"セカンダリ アクションのみ選択できます"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"デコレーション テスト"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"セカンダリ アクションとデコレーション"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"行も選択できます"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"セカンダリ アクションを選択しました"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"行のプライマリ アクションを選択しました"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"その他のテンプレートのデモ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"デモを表示"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"テンプレート レイアウトのデモ"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access のデモ画面"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ユーザー操作"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"権限リクエストのデモ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"アプリ オーバーフロー バリデータ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"変更を行う際に以下のテンプレートをテストしてください"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"車両の状態が駐車中から運転状態に変わりました"</string>
     <string name="perm_group" msgid="3834918337351876270">"権限グループ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ショーケース アプリの権限グループ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"精細な位置情報へのアクセス"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
index 0db9ab0..3a78f70 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"მიღწეულია ამოცანების ლიმიტი\nგაგრძელება გამოიწვევს აპის ძალით შეჩერებას"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"ამოცანის ეტაპი: %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"დააწკაპუნეთ გასაგრძელებლად"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"გთხოვთ, ეწვიოთ სხვადასხვა შაბლონებს, რათა დარწმუნდეთ, რომ მანქანა საავტომობილო რეჟიმშია"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"გადართვა ღილაკის დემო"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"გადამრთველის ტესტი"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"სტატუსის ცვლილებები ნებადართულია"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"განახლებებმა დაუშვა უკან დაბრუნება."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"გადამრთველის ტესტი ჩართულია"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"გადამრთველის ტესტი გათიშულია"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"მეორადი მოქმედების და დეკორაციის დემო"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"მეორადი მოქმედების ტესტი"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"შესაძლებელია მხოლოდ მეორადი მოქმედების არჩევა"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"დეკორაციის ტესტი"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"მეორადი მოქმედებები და დეკორაცია"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ასევე შეიძლება მწკრივის არჩევა"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"არჩეულია მეორადი მოქმედება"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"არჩეულია მწკრივის პირველადი მოქმედება"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"სხვადასხვა შაბლონური დემოები"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"დემოების ჩვენება"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"შაბლონის განლაგების დემო"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"ხმის წვდომის დემო ეკრანი"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"მომხმარებლის ინტერაქციები"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"მოითხოვეთ ნებართვების დემო"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"აპლიკაციების გადატვირთვის ვალიდატორი"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"გთხოვთ, შეამოწმოთ შემდეგი შაბლონები ცვლილებისას"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ავტომობილი პარკირებულიდან მართვის მდგომარეობამდე"</string>
     <string name="perm_group" msgid="3834918337351876270">"ნებართვის ჯგუფი"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ნებართვის ჯგუფი Showcase აპისთვის"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"წვდომა კარგ მდებარეობაზე"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
index fbec90e..fe5649b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Тапсырма өз шегіне жетті.\nІлгері жүрсеңіз, қолданба күштеп тоқтатылады."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d/%2$d тапсырма қадамы"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Ілгері жүру үшін басыңыз."</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Әртүрлі үлгілерді қарап, автокөліктің жүргізу режимінде тұрғанына көз жеткізіңіз."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Ауыстыру түймесі (демо нұсқасы)"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Ауыстырғыш сынағы"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Күй бақыланатын өзгерістерге рұқсат берілген."</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"\"Артқа\" операциясында өзгертуге рұқсат етілген"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Ауыстырғыш сынағы қосылған."</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Ауыстырғыш сынағы өшірілген."</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Қосымша әрекеттің және безендірудің демо нұсқасы"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Қосымша әрекетті сынау"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Қосымша әрекет қана таңдалады."</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Безендіруді сынау"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Қосымша әрекеттер және безендіру"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Жолды да таңдауға болады."</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Қосымша әрекет таңдалды."</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Жол үшін негізгі әрекет таңдалды."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"\"Басқалары\" үлгісінің демо нұсқасы"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Презентацияның демо нұсқасы"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Формат үлгісі (демо нұсқалары)"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access демо нұсқасының экраны"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Пайдаланушы әрекеттері"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Рұқсат сұрау операциясының демо нұсқасы"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Қолданбаның қосымша мәзірін тексеру құралы"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Көлікті тұраққа қойылған күйден жүргізу күйіне өзгерту кезінде"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"келесі үлгілерді тексеріп көріңіз."</string>
     <string name="perm_group" msgid="3834918337351876270">"Рұқсат тобы"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase қолданбасының рұқсат тобы"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Нақты локацияны пайдалану"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
index 99666c01..afb1fc5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"បានឈានដល់​ដែនកំណត់កិច្ចការហើយ\nការបន្តទៀត​នឹងបង្ខំឱ្យកម្មវិធីបញ្ឈប់"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"ជំហានកិច្ចការទី %1$d នៃ %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ចុចដើម្បីបន្ត"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"សូមចូលមើលទម្រង់គំរូផ្សេងៗ និងធ្វើឱ្យប្រាកដថា រថយន្តស្ថិតក្នុងមុខងារបើកបរ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"គំរូបង្ហាញនៃប៊ូតុងបិទ/បើក"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ការធ្វើតេស្ត​ប៊ូតុងបិទ/បើក"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ការផ្លាស់ប្ដូរ​តាមស្ថានភាពត្រូវបានអនុញ្ញាត"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"បានអនុញ្ញាត​ការដំឡើងកំណែ​នៅលើ​ប្រតិបត្តិការ​ថយក្រោយ។"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"បានបើកការធ្វើតេស្ត​ប៊ូតុងបិទ/បើក"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"បានបិទការធ្វើតេស្ត​ប៊ូតុងបិទ/បើក"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"គំរូបង្ហាញការតុបតែង និងសកម្មភាពបន្ទាប់បន្សំ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"តេស្តសកម្មភាពបន្ទាប់បន្សំ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"អាចជ្រើសរើសបានតែសកម្មភាពបន្ទាប់បន្សំប៉ុណ្ណោះ"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"តេស្តនៃការតុបតែង"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ការតុបតែង និងសកម្មភាពបន្ទាប់បន្សំ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ជួរដេកក៏អាចត្រូវបានជ្រើសរើសផងដែរ"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"សកម្មភាពបន្ទាប់បន្សំត្រូវបានជ្រើសរើស"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"សកម្មភាពចម្បងជួរដេកត្រូវបានជ្រើសរើស"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"គំរូបង្ហាញនៃ​ទម្រង់គំរូផ្សេងៗ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"គំរូបង្ហាញអំពីការតាំងរំលេច"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"គំរូបង្ហាញនៃប្លង់​ទម្រង់គំរូ"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"អេក្រង់គំរូបង្ហាញនៃ​ការចូលប្រើប្រាស់តាមរយៈសំឡេង"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"អន្តរកម្មរបស់អ្នកប្រើប្រាស់"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"គំរូបង្ហាញនៃ​ការស្នើសុំការអនុញ្ញាត"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ឧបករណ៍បញ្ជាក់ភាពលើសចំណុះកម្មវិធី"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"សូមធ្វើតេស្តទម្រង់គំរូដូចខាងក្រោម ពេលផ្លាស់ប្ដូរ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"យានជំនិះពីស្ថានភាពចតទៅជាស្ថានភាពបើកបរ"</string>
     <string name="perm_group" msgid="3834918337351876270">"ក្រុមអនុញ្ញាត"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ក្រុមអនុញ្ញាតសម្រាប់កម្មវិធី Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"សិទ្ធិចូលប្រើ​ទីតាំងជាក់លាក់"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
index c85df6b..196f9f0 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ಕಾರ್ಯದ ಮಿತಿಯನ್ನು ತಲುಪಿದ್ದೀರಿ\nಮುಂದುವರಿಸಿದರೆ ಆ್ಯಪ್ ಅನ್ನು ಬಲವಂತವಾಗಿ ನಿಲ್ಲಿಸಲಾಗುತ್ತದೆ"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"ಕಾರ್ಯದ ಹಂತ %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ಮುಂದೆ ಹೋಗಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"ವಿವಿಧ ಟೆಂಪ್ಲೇಟ್‌ಗಳಿಗೆ ಭೇಟಿ ನೀಡಿ ಮತ್ತು ಕಾರು, ಡ್ರೈವಿಂಗ್ ಮೋಡ್‌ನಲ್ಲಿರುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ಟಾಗಲ್ ಬಟನ್ ಡೆಮೋ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ಟಾಗಲ್ ಟೆಸ್ಟ್"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ಪುನಃ ಸ್ಥಾಪಿಸಬಹುದಾದ ಬದಲಾವಣೆಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"ಹಿನ್ನೆಲೆ ಕಾರ್ಯಾಚರಣೆಗಳಿಗಾಗಿ ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"ಟಾಗಲ್ ಟೆಸ್ಟ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"ಟಾಗಲ್ ಟೆಸ್ಟ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆ ಮತ್ತು ಅಲಂಕಾರ ಡೆಮೊ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಯ ಪರೀಕ್ಷೆ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಯನ್ನು ಮಾತ್ರ ಆಯ್ಕೆ ಮಾಡಬಹುದು"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ಅಲಂಕಾರ ಪರೀಕ್ಷೆ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಅಲಂಕಾರ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ಸಾಲನ್ನು ಸಹ ಆಯ್ಕೆ ಮಾಡಬಹುದು"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"ಸಾಲಿನ ಪ್ರಾಥಮಿಕ ಕ್ರಿಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ಇತರ ಟೆಂಪ್ಲೇಟ್‌ಗಳ ಡೆಮೋಗಳು"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase ಡೆಮೋಗಳು"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ಟೆಂಪ್ಲೇಟ್ ಲೇಔಟ್ ಡೆಮೋಗಳು"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"ಧ್ವನಿ ಆ್ಯಕ್ಸೆಸ್ ಡೆಮೋ ಸ್ಕ್ರೀನ್"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ಬಳಕೆದಾರರ ಸಂವಹನಗಳು"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ಅನುಮತಿಗಳ ಡೆಮೋಗಳನ್ನು ವಿನಂತಿಸಿ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ಅಪ್ಲಿಕೇಶನ್ ಓವರ್‌ಫ್ಲೋ ಮೌಲ್ಯಮಾಪಕ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ಬದಲಾಯಿಸುವಾಗ ಕೆಳಗಿನ ಟೆಂಪ್ಲೇಟ್‌ಗಳನ್ನು ಪರೀಕ್ಷಿಸಿ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ನಿಲುಗಡೆಯಿಂದ ಚಾಲನೆಯ ಸ್ಥಿತಿಗೆ ಬಂದ ವಾಹನ"</string>
     <string name="perm_group" msgid="3834918337351876270">"ಅನುಮತಿ ಗುಂಪು"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ಶೋಕೇಸ್ ಆ್ಯಪ್‌ನ ಅನುಮತಿ ಗುಂಪು"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"ನಿಖರವಾದ ಸ್ಥಳಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
index 9aa1ed8..20f9cf1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"작업 제한에 도달함\n계속 진행하면 앱이 강제 종료됩니다"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"작업 단계 %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"클릭하여 진행"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"다른 템플릿을 방문하여 자동차가 운전 모드인지 확인하세요."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"전환 버튼 데모"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"전환 테스트"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"스테이트풀(Stateful) 변경은 허용됨"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"뒤로 작업에서 업데이트가 허용되었습니다."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"전환 테스트 사용 설정됨"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"전환 테스트 사용 중지됨"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"보조 작업 및 장식 데모"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"보조 작업 테스트"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"보조 작업만 선택할 수 있습니다."</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"장식 테스트"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"보조 작업 및 장식"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"행도 선택할 수 있습니다."</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"보조 작업 선택됨"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"행 기본 작업 선택됨"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"기타 템플릿 데모"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"쇼케이스 데모"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"템플릿 레이아웃 데모"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"음성 액세스 데모 화면"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"사용자 상호작용"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"권한 요청 데모"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"애플리케이션 오버플로 검사기"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"변경하는 동안 다음 템플릿을 테스트하세요."</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"차량을 주차 상태에서 운전 상태로"</string>
     <string name="perm_group" msgid="3834918337351876270">"권한 그룹"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"쇼케이스 앱에 대한 권한 그룹"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"정확한 위치 액세스"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
index 486cba8..4c8ad5c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Тапшырманын чегине жетти\nАлдыга жылсаңыз, колдонмо мажбурлап токтотулат"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Тапшырма кадамы %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Алдыга өтүү үчүн чыкылдатыңыз"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Башка үлгүлөргө өтүп, унаа айдоо режиминде экенин текшериңиз"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Күйгүзүү/өчүрүү баскычынын демосу"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Өчүрүү/күйгүзүү сыноосу"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Абалы сакталган өзгөртүүлөргө уруксат берилген"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Артка операцияларында жаңыртууларга уруксат берет."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Өчүрүү/күйгүзүү сыноосу иштетилди"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Өчүрүү/күйгүзүү сыноосу өчүрүлдү"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Кошумча аракет жана Жасалгалоо демосу"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Кошумча аракетти сыноо"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Кошумча аракетти гана тандоого болот"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Жасалгалоону сыноо"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Кошумча аракеттер жана Жасалгалоо"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Сапты да тандоого болот"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Кошумча аракет тандалды"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Негизги аракет сабы тандалды"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Башка үлгүлөрдүн демолору"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase демолору"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Үлгү калыптарынын демолору"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access демо экраны"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Колдонуучунун мамилелери"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демо версияларга уруксат суроо"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Ашыкча колдонмолорду текшергич"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Өзгөртүп жатканда төмөнкү үлгүлөрдү сынап көрүңүз:"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"токтоп турган унааны айдоо абалы"</string>
     <string name="perm_group" msgid="3834918337351876270">"Уруксаттар тобу"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Колдонмону көрсөтүү үчүн уруксаттар тобу"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Так жайгашкан жерди көрүү"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
index c1ad5d5..1b5d9ec 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ຮອດຂີດຈຳກັດໜ້າວຽກແລ້ວ\nຕໍ່ໄປຈະບັງຄັບຢຸດແອັບ"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"ຂັ້ນຕອນໜ້າວຽກທີ %1$d ຈາກທັງໝົດ %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ຄລິກເພື່ອໄປໜ້າ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"ກະລຸນາເຂົ້າໄປແມ່ແບບອື່ນ ແລະ ກວດໃຫ້ແນ່ໃຈວ່າລົດຢູ່ໃນໂໝດຂັບຂີ່"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ເດໂມປຸ່ມເປີດປິດ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ສະຫຼັບການທົດສອບ"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ອະນຸຍາດໃຫ້ມີການປ່ຽນແປງສະຖານະໄດ້"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"ອະນຸຍາດໃຫ້ອັບເດດໃນການດຳເນີນການເບື້ອງຫຼັງ."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"ເປີດການນຳໃຊ້ການທົດສອບການສະຫຼັບ"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"ປິດການນຳໃຊ້ການທົດສອບການສະຫຼັບ"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ການສາທິດຄຳສັ່ງສຳຮອງ ແລະ ການຕົກແຕ່ງ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ການທົດສອບຄຳສັ່ງສຳຮອງ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ສາມາດເລືອກໄດ້ສະເພາະຄຳສັ່ງສຳຮອງເທົ່ານັ້ນ"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ການທົດສອບການຕົກແຕ່ງ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ຄຳສັ່ງສຳຮອງ ແລະ ການຕົກແຕ່ງ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ນອກຈາກນັ້ນຍັງເລືອກແຖວໄດ້ນຳ"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ເລືອກຄຳສັ່ງສຳຮອງຂອງແຖວແລ້ວ"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"ເລືອກຄຳສັ່ງຫຼັກຂອງແຖວແລ້ວ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ເດໂມແມ່ແບບອື່ນໆ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ເດໂມ Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ເດໂມໂຄງຮ່າງແມ່ແບບ"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"ໜ້າຈໍເດໂມການເຂົ້າເຖິງສຽງ"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ການໂຕ້ຕອບຂອງຜູ້ໃຊ້"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ຮ້ອງຂໍການອະນຸຍາດເດໂມ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ໂປຣແກຣມກວດສອບລາຍການເພີ່ມເຕີມຂອງແອັບພລິເຄຊັນ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ກະລຸນາທົດສອບແມ່ແບບຕໍ່ໄປນີ້ໃນຂະນະທີ່ປ່ຽນແປງ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ສະຖານະຂອງພາຫະນະຈາກຈອດຢູ່ເປັນກຳລັງຂັບຂີ່"</string>
     <string name="perm_group" msgid="3834918337351876270">"ກຸ່ມການອະນຸຍາດ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ກຸ່ມການອະນຸຍາດສຳລັບແອັບ Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"ສິດເຂົ້າເຖິງສະຖານທີ່ແບບລະອຽດ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
index ac2a584..31e4669 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Pasiektas užduočių apribojimas\nTęsiant programa bus priverstinai sustabdyta"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d užduoties veiksmas iš %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Spustelėkite, jei norite tęsti"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Peržiūrėkite kitus šablonus ir įsitikinkite, kad automobilis veikia vairavimo režimu"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Perjungimo mygtuko demonstracinė versija"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Perjungti bandymą"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Leidžiami būsenos pakeitimai"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Naujiniai leidžiami ankstesnėms operacijoms."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Bandymo perjungimas įgalintas"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Bandymo perjungimas išjungtas"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Antrinio veiksmo ir dekoracijų demonstracinė versija"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Antrinio veiksmo bandymas"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Galima pasirinkti tik antrinį veiksmą"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekoracijų bandymas"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Antriniai veiksmai ir dekoracijos"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Taip pat galima pasirinkti eilutę"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Pasirinktas antrinis veiksmas"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Pasirinktas pagrindinis eilutės veiksmas"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Įvairių šablonų demonstracinės versijos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Rodyti demonstracines versijas"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Šablonų išdėstymo demonstracinės versijos"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Prieigos balsu demonstracinės versijos ekranas"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Naudotojo sąveikos"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Leidimų prašymo demonstracinės versijos"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Programos perpildymo patvirtinimo priemonė"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Išbandykite toliau nurodytus šablonus įkraudami"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"transporto priemonė iš stovėjimo į važiavimo būseną"</string>
     <string name="perm_group" msgid="3834918337351876270">"Leidimų grupė"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Demonstracinės programos leidimų grupė"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Prieiga prie tikslios vietovės"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
index baa904c..6b4b746 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Sasniegts uzdevumu ierobežojums\nTurpinot lietotnes darbība tiks apturēta piespiedu kārtā."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Uzdevuma darbība: %1$d. no %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Noklikšķiniet, lai dotos tālāk"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Lūdzu, skatiet dažādas veidnes un nodrošiniet, ka automašīna ir braukšanas režīmā."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Pārslēgšanas pogas demonstrācija"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Pārslēgšanas tests"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Stāvokļa izmaiņas ir atļautas"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Pārejai atpakaļ ir atļauti atjauninājumi."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Pārslēgšanas tests ir iespējots"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Pārslēgšanas tests ir atspējots"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekundārās darbības un noformējuma demonstrācija"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Sekundārās darbības tests"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Atlasīt var tikai sekundāro darbību"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Noformējuma tests"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundārās darbības un noformējums"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Arī rindu var atlasīt"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Atlasīta sekundārā darbība"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Atlasīta rindas primārā darbība"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Dažādu veidņu demonstrācijas"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase demonstrācijas"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Veidņu izkārtojumu demonstrācijas"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Balss piekļuves demonstrācijas ekrāns"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Lietotāju mijiedarbība"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstrācija: atļauju pieprasīšana"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Lietojumprogrammu pārpildes pārbaudes rīks"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Lūdzu, testējiet tālāk norādītās veidnes, kamēr maināt"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"transportlīdzekļa režīmu no apturēta uz braukšanas režīmu."</string>
     <string name="perm_group" msgid="3834918337351876270">"Atļauju grupa"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Atļauju grupa demonstrācijas lietotnei"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Piekļuve precīzai atrašanās vietai"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
index 8bbd60e..4eab700 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнато е ограничувањето за задачи\nАко продолжите, апликацијата ќе се исклучи присилно"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Преземете чекор %1$d од %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Кликнете за да одите нанапред"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Одете на различните шаблони и погрижете се автомобилот да е во режим на возење"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Демо на копче за вклучување/исклучување"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Тест за вклучување/исклучување"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Дозволени се промени на зачувувањето на состојбата"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Ажурирањата се дозволени при операции на враќање назад."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Овозможено тест за вклучување/исклучување"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Оневозможено тест за вклучување/исклучување"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демо за секундарно дејство и украсување"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Проба за секундарно дејство"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Само секундарното дејство може да се избере"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Проба за украсување"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Секундарни дејства и украсување"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Редот исто така може да се избере"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Избрано е секундарно дејство"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Избрано е примарно дејство на ред"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Разни демоа за шаблони"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демоа за прикажување"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демоа за распоред на шаблони"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Екран за демо за „Пристап со глас“"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Кориснички интеракции"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Побарајте дозволи за демо"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Алатка за потврдување прелевање на апликацијата"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Тестирајте ги следните шаблони додека менувате"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"возилото од паркирана во состојба на возење"</string>
     <string name="perm_group" msgid="3834918337351876270">"Група дозволи"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Група дозволи за апликација за вести избрани од издавачи"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Пристап до точна локација"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
index 0cfe4d8..1fe8d05 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ടാസ്ക്ക് പരിധിയെത്തി\nമുന്നോട്ട് പോകുന്നത് ആപ്പ് നിർബന്ധിതമായി നിർത്താൻ കാരണമാകും"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d ടാസ്ക്ക് ഘട്ടങ്ങളിൽ %1$d -ാമത്തേത്"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"മുന്നോട്ട് പോകാൻ ക്ലിക്ക് ചെയ്യുക"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"വ്യത്യസ്ത ടെംപ്ലേറ്റുകൾ സന്ദർശിച്ച്, ഡ്രൈവിംഗ് മോഡിലാണ് കാർ ഉള്ളതെന്ന് ഉറപ്പാക്കുക"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ടോഗിൾ ബട്ടൺ ഡെമോ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ടെസ്റ്റ് മാറ്റുക"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"സ്റ്റേറ്റ്ഫുള്ളായ മാറ്റങ്ങൾ അനുവദനീയമാണ്"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access ഡെമോ സ്ക്രീൻ"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ഉപയോക്തൃ ഇടപഴകലുകൾ"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"അനുമതി ഡെമോകൾ അഭ്യർത്ഥിക്കുക"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ആപ്ലിക്കേഷൻ ഓവർഫ്ലോ വാലിഡേറ്റർ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"പാർക്കിംഗിൽ നിന്ന് ഡ്രൈവിംഗ് നിലയിലേക്ക് വാഹനം"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"മാറ്റുമ്പോൾ, ഇനിപ്പറയുന്ന ടെംപ്ലേറ്റുകൾ പരിശോധിക്കുക"</string>
     <string name="perm_group" msgid="3834918337351876270">"അനുമതി ഗ്രൂപ്പ്"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase ആപ്പിനുള്ള അനുമതി ഗ്രൂപ്പ്"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"ഫൈൻ ലൊക്കേഷനിലേക്കുള്ള ആക്സസ്"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
index faf65e2..512e67c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Ажлын хязгаарт хүрсэн\nҮргэлжлүүлснээр аппыг хүчээр зогсооно"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Ажлын %2$d-н %1$d-р алхам"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Үргэлжлүүлэхийн тулд товшино уу"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Өөр загварт зочилж, машин нь втомашин жолоодох горимд байгаа эсэхийг баталгаажуулна уу"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Асаах товчийн демо"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Асаах/Унтраах туршилт"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Төлөвтэй өөрчлөлтийг зөвшөөрнө"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Шинэчлэлтүүдийг арын үйл ажиллагаанд хийхийг зөвшөөрдөг."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Асаах/Унтраах туршилтыг идэвхжүүлсэн"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Асаах/Унтраах туршилтыг идэвхгүй болгосон"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Хоёрдогч үйлдэл ба чимэглэлийн демо"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Хоёрдогч үйлдлийн тест"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Зөвхөн хоёрдогч үйлдлийг сонгох боломжтой"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Чимэглэлийн туршилт"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Хоёрдогч үйлдлүүд ба чимэглэл"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Мөрийг мөн сонгож болно"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Хоёрдогч үйлдлийг сонгосон"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Мөрийн үндсэн үйлдлийг сонгосон"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Холимог загварын демо"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase-н демо"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Загварын бүдүүвч бүхий демо"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access-н демо дэлгэц"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Хэрэглэгчийн харилцан үйлдлүүд"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Зөвшөөрлийн демогийн хүсэлт тавих"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Аппликэйшний илүү ашиглалтыг баталгаажуулагч"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Цэнэглэх үедээ дараах загварыг туршина уу"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"зогсоолд байснаас жолоодох төлөв рүү сэлгэсэн тээврийн хэрэгсэл"</string>
     <string name="perm_group" msgid="3834918337351876270">"Зөвшөөрлийн бүлэг"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase аппын зөвшөөрлийн бүлэг"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Нарийвчилсан байршилд хандах"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
index d95f041..36e204a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"टास्कची मर्यादा गाठली आहे\nपुढे सुरू ठेवल्यास, अ‍ॅप सक्तीने थांबवले जाईल"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"टास्कची %1$d पैकी %2$d पायरी"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"पुढे जाण्यासाठी क्लिक करा"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"कृपया विविध टेंप्लेटना भेट द्या आणि कार ही ड्रायव्हिंग मोडमध्ये असल्याची खात्री करा"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"टॉगल बटण डेमो"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"टॉगल करण्याशी संबंधित चाचणी"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"स्थितीशी संबंधित बदलांना अनुमती आहे"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"बॅक ऑपरेशनवर अपडेटना अनुमती दिली आहे."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"टॉगल करण्याशी संबंधित चाचणी सुरू केली"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"टॉगल करण्याशी संबंधित चाचणी बंद केली"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"दुय्यम कृती आणि सजावट डेमो"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"दुय्यम कृती चाचणी"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"फक्त दुय्यम कृती निवडली जाऊ शकते"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"सजावट चाचणी"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"दुय्यम कृती आणि सजावट"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"पंक्तीदेखील निवडली जाऊ शकते"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"दुय्यम कृती निवडली आहे"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"पंक्तीशी संबंधित प्राथमिक कृती निवडली आहे"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"इतर टेंप्लेटचे डेमो"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"डेमो दाखवा"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"टेंप्लेट लेआउट डेमो"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access डेमो स्क्रीन"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"वापरकर्ता संवाद"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"परवानग्या डेमोची विनंती करा"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"अ‍ॅप्लिकेशन ओव्हरफ्लो व्हॅलिडेटर"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"बदलताना कृपया पुढील टेंप्लेटची चाचणी घ्या"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"कार पार्क करण्यापासून ते ड्रायव्हिंगच्या परिस्थितीपर्यंत"</string>
     <string name="perm_group" msgid="3834918337351876270">"परवानगी गट"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase अ‍ॅपसाठी परवानगी गट"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"अचूक स्थान याचा अ‍ॅक्सेस"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
index 1530d05..afd924c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Had tugasan dicapai\nMaju ke hadapan akan henti paksa apl"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Langkah tugasan %1$d daripada %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik untuk maju"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Sila lawati templat yang berbeza dan pastikan kereta berada dalam mod memandu"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo Butang Togol"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Togol ujian"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Perubahan keadaan dibenarkan"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Kemaskinian dibenarkan pada operasi belakang."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Ujian Togol Didayakan"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Ujian Togol Dilumpuhkan"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Tindakan Sekunder dan Demonstrasi Perhiasan"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Ujian Tindakan Sekunder"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Hanya tindakan sekunder boleh dipilih"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Ujian Perhiasan"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Tindakan Sekunder dan Perhiasan"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Baris ini juga boleh dipilih"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Tindakan Sekunder dipilih"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Tindakan utama baris dipilih"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Pelbagai Demo Templat"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo Wadah"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Reka Letak Templat"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Skrin Demo Akses Suara"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaksi Pengguna"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demo Minta Kebenaran"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Pengesah Limpahan Aplikasi"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Sila uji templat yang berikut semasa membuat penukaran"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"kenderaan daripada keadaan diletakkan kepada memandu"</string>
     <string name="perm_group" msgid="3834918337351876270">"Kumpulan Kebenaran"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Kumpulan Kebenaran untuk Apl Wadah"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Akses kepada Lokasi Halus"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
index da967bc0c..f005fa7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"လုပ်ဆောင်စရာကန့်သတ်ချက် ရောက်သွားပြီ\nရှေ့ဆက်သွားခြင်းက အက်ပ်ကို မဖြစ်မနေရပ်ခိုင်းမည်"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"လုပ်ဆောင်စရာအဆင့် %2$d ခုအနက် %1$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ရှေ့ဆက်သွားရန် နှိပ်ပါ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"ပုံစံအမျိုးမျိုးကို ဝင်ကြည့်ပြီး ကားသည် မောင်းနှင်မုဒ်တွင်ရှိကြောင်း သေချာပါစေ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ဖွင့်ပိတ် ခလုတ် သရုပ်ပြချက်"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"စစ်ဆေးမှု ပြောင်းခြင်း"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"အနေအထားအပြည့် အပြောင်းအလဲများ ခွင့်ပြုထားသည်"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"ပြန်ရွှေ့ခြင်းများတွင် အပ်ဒိတ်များကို ခွင့်ပြုသည်။"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"စမ်းသပ်ခလုတ် ဖွင့်ထားသည်"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"စမ်းသပ်ခလုတ် ပိတ်ထားသည်"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ဒုတိယဦးစားပေး လုပ်ဆောင်ချက်နှင့် တန်ဆာဆင်မှု သရုပ်ပြခြင်း"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ဒုတိယဦးစားပေးလုပ်ဆောင်ချက် စမ်းသပ်ခြင်း"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ဒုတိယဦးစားပေးလုပ်ဆောင်ချက်ကိုသာ ရွေးနိုင်သည်"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"တန်ဆာဆင်မှု စမ်းသပ်ခြင်း"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ဒုတိယဦးစားပေးလုပ်ဆောင်ချက်များနှင့် တန်ဆာဆင်ခြင်း"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"အတန်းကိုလည်း ရွေးနိုင်သည်"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ဒုတိယဦးစားပေး လုပ်ဆောင်ချက်ကို ရွေးထားသည်"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"အတန်း၏ ပထမဦးစားပေး လုပ်ဆောင်ချက်ကို ရွေးထားသည်"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"အထွေထွေ ပုံစံသရုပ်ပြချက်များ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase သရုပ်ပြချက်များ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"နမူနာပုံစံ သရုပ်ပြချက်များ"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access သရုပ်ပြချက်စခရင်"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"အသုံးပြုသူ ပြန်လှန်တုံ့ပြန်မှုများ"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ခွင့်ပြုချက်များ တောင်းဆိုရန် သရုပ်ပြချက်"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"အပလီကေးရှင်းအပို စိစစ်မှုစနစ်"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"အားသွင်းစဉ်အတွင်း အောက်ပါပုံစံများကို စမ်းသပ်ပါ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ယာဉ်ရပ်နားသည်မှ မောင်းနှင်သည့် အခြေအနေသို့"</string>
     <string name="perm_group" msgid="3834918337351876270">"ခွင့်ပြုချက်အုပ်စု"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"‘စင်မြင့် အက်ပ်’ အတွက် ခွင့်ပြုချက်အုပ်စု"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"‘တည်နေရာအချော’ သုံးခွင့်"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
index 44e9320..8936507 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Oppgavegrensen er nådd\nHvis du fortsetter, blir appen tvunget til å avslutte"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Oppgavetrinn %1$d av %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klikk for å fortsette"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Gå til de ulike malene, og forsikre deg om at bilen er i kjøremodus"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo av av/på-knapp"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test av/på-knapp"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Dynamiske endringer er tillatt"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Oppdateringer tillates ved tilbakeoperasjoner."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test av av/på-knapp er aktivert"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test av av/på-knapp er deaktivert"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo av sekundærhandling og dekorasjon"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test av sekundærhandling"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Bare sekundærhandlingen kan velges"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekorasjonstest"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundærhandling og dekorasjon"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Raden kan også velges"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundærhandlingen er valgt"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Hovedhandlingen til raden er valgt"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demoer av diverse maler"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demoer i fokus"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demoer av mallayout"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Demoskjerm for Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Brukerinteraksjoner"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demoer for forespørsler om tillatelser"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Valideringsverktøy for appoverflyt"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Test disse malene når du bytter"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"fra parkerings- til kjøretilstand for kjøretøyet"</string>
     <string name="perm_group" msgid="3834918337351876270">"Tillatelsesgruppe"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Tillatelsesgruppe for Showcase-appen"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Tilgang til nøyaktig posisjon"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
index 19fcf1e..82567d5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"कार्यको सीमा सकियो\nतपाईं अगाडि बढ्नुभयो भने एप जबरजस्ती रोकिने छ"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d कार्यको %1$d औँ चरण"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"अगाडि बढ्न क्लिक गर्नुहोस्"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"कृपया फरक-फरक टेम्प्लेट प्रयोग गर्नुहोस् र कार ड्राइभिङ मोडमा छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"टगल गर्ने बटनको डेमो"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"टगल टेस्ट"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"स्टेटफुल परिवर्तन गर्ने अनुमति छ"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"अघिल्लो पेजमा गई परिवर्तन गर्न सकिन्छ।"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"टगल टेस्ट अन गरिएको छ"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"टगल टेस्ट अफ गरिएको छ"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"सेकेन्डरी एक्सन तथा डेकोरेसन डेमो"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"सेकेन्डरी एक्सन टेस्ट"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"सेकेन्डरी एक्सन मात्र चयन गर्न सकिन्छ"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"डेकोरेसन टेस्ट"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"सेकेन्डरी एक्सन तथा डेकोरेसन"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"रो पनि चयन गर्न सकिन्छ"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"सेकेन्डरी एक्सन चयन गरिएको छ"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"रोको प्राइमरी एक्सन चयन गरिएको छ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"टेम्प्लेटका विविध डेमोहरू"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"सोकेसहरूको डेमो"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"टेम्प्लेट लेआउटका डेमोहरू"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access को डेमो स्क्रिन"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"प्रयोगकर्ताले गरेका अन्तर्क्रिया"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"अनुमतिहरूका डेमोहरू माग्नुहोस्"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ऐप्लिकेसन ओभरफ्लो भ्यालिडेटर"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"कृपया SIM बदलक्ने बेलामा निम्न टेम्प्लेटहरूको परिक्षण गर्नुहोस्"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"पार्क गरिएको अवस्थाबाट चलाउने अवस्थामा लगिएको कार"</string>
     <string name="perm_group" msgid="3834918337351876270">"अनुमतिको समूह"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase एपलाई दिइएको अनुमतिको समूह"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"सटीक लोकेसन प्रयोग गर्ने अनुमति"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
index 076d9d2..41c1879 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Taaklimiet bereikt\nAls je doorgaat, wordt de app geforceerd gestopt"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Taakstap %1$d van %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik om verder te gaan"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Open de verschillende templates en zorg dat de auto in de rijstand staat"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo voor schakelaar"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test aan-/uitzetten"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Stateful wijzigingen zijn toegestaan"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Updates toegestaan bij teruggaan in de weergavestapel."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Schakeltest aangezet"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Schakeltest uitgezet"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo voor secundaire actie en decoratie"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test voor secundaire actie"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Alleen de secundaire actie kan worden geselecteerd"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test voor decoratie"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Secundaire acties en decoratie"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"De rij kan ook worden geselecteerd"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Secundaire actie is geselecteerd"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Rij voor primaire actie is geselecteerd"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Diverse templatedemo\'s"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo\'s laten zien"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo\'s voor opmaaktemplate"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Demoscherm voor Spraaktoegang"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Gebruikersinteracties"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Rechtendemo\'s aanvragen"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Application Overflow Validator"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Test de volgende templates terwijl je het voertuig"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"van de parkeerstand in de rijstand zet"</string>
     <string name="perm_group" msgid="3834918337351876270">"Rechtengroep"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Rechtengroep voor Showcase-app"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Toegang tot exacte locatie"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
index 93bc94e..4a7ea67 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ଟାସ୍କର ସୀମାରେ ପହଞ୍ଚିଯାଇଛି\nଆଗକୁ ଗଲେ ଆପଟି ବାଧ୍ୟତାର ସହ ବନ୍ଦ ହୋଇଯିବ"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$dଟିରୁ %1$d ନମ୍ବର ଟାସ୍କର ଷ୍ଟେପ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ଆଗକୁ ଯିବା ପାଇଁ କ୍ଲିକ କରନ୍ତୁ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"ଦୟାକରି ବିଭିନ୍ନ ଟେମ୍ପଲେଟକୁ ଭିଜିଟ କରନ୍ତୁ ଏବଂ କାରଟି ଡ୍ରାଇଭିଂ ମୋଡରେ ଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ଟୋଗଲ ବଟନର ଡେମୋ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ପରୀକ୍ଷା ଟୋଗଲ କରନ୍ତୁ"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ଷ୍ଟେଟଫୁଲ ପରିବର୍ତ୍ତନଗୁଡ଼ିକୁ ଅନୁମତି ଦିଆଯାଇଛି"</string>
@@ -307,35 +306,24 @@
     <string name="additional_data_text" msgid="2846223398214158872">"ପୃଷ୍ଠପଟର ଅପରେସନଗୁଡ଼ିକରେ ଅପଡେଟଗୁଡ଼ିକୁ ଅନୁମତି ଦିଆଯାଇଛି।"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"\"ପରୀକ୍ଷା ଟୋଗଲ କରିବା\" ସକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"\"ପରୀକ୍ଷା ଟୋଗଲ କରିବା\" ଅକ୍ଷମ କରାଯାଇଛି"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଏବଂ ଡେକୋରେସନ ଡେମୋ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଟେଷ୍ଟ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"କେବଳ ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟକୁ ଚୟନ କରାଯାଇପାରିବ"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ଡେକୋରେସନ ଟେଷ୍ଟ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଏବଂ ଡେକୋରେସନ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ଏହି ଧାଡ଼ିଟିକୁ ମଧ୍ୟ ଚୟନ କରାଯାଇପାରିବ"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଚୟନ କରାଯାଇଛି"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"ଧାଡ଼ିର ପ୍ରାଥମିକ କାର୍ଯ୍ୟ ଚୟନ କରାଯାଇଛି"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ବିବିଧ ଟେମ୍ପଲେଟର ଡେମୋ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase ଡେମୋ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ଟେମ୍ପଲେଟ ଲେଆଉଟର ଡେମୋଗୁଡ଼ିକ"</string>
     <string name="grid_template_menu_demo_title" msgid="7096285873490705119">"ଗ୍ରିଡ ଟେମ୍ପଲେଟର ଡେମୋଗୁଡ଼ିକ"</string>
     <string name="voice_access_demo_title" msgid="3825223890895361496">"ଡେମୋ ସ୍କ୍ରିନକୁ ଭଏସ ଆକ୍ସେସ"</string>
-    <string name="user_interactions_demo_title" msgid="1356952319161314986">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଇଣ୍ଟରାକ୍ସନ"</string>
+    <string name="user_interactions_demo_title" msgid="1356952319161314986">"ୟୁଜର ଇଣ୍ଟରାକ୍ସନ"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ଅନୁମତିର ଡେମୋ ପାଇଁ ଅନୁରୋଧ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ଆପ୍ଲିକେସନ ଓଭରଫ୍ଲୋ ଭାଲିଡେଟର"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ପରିବର୍ତ୍ତନ କରିବା ବେଳେ ଦୟାକରି ନିମ୍ନୋକ୍ତ ଟେମ୍ପଲେଟଗୁଡ଼ିକୁ ପରୀକ୍ଷା କରନ୍ତୁ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ପାର୍କ କରାଯାଇଥିବାରୁ ଡ୍ରାଇଭିଂ ସ୍ଥିତି ପର୍ଯ୍ୟନ୍ତ ଗାଡ଼ି"</string>
     <string name="perm_group" msgid="3834918337351876270">"ଅନୁମତି ଗୋଷ୍ଠୀ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase ଆପ ପାଇଁ ଅନୁମତି ଗୋଷ୍ଠୀ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"ସଠିକ୍ ଲୋକେସନକୁ ଆକ୍ସେସ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
index 3fb771d..80e73db 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ਕਾਰਜ ਸੀਮਾ ਪੂਰੀ ਹੋ ਗਈ\nਜਾਰੀ ਰੱਖਣ \'ਤੇ ਐਪ ਜ਼ਬਰਦਸਤੀ ਬੰਦ ਹੋ ਜਾਵੇਗੀ"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d ਵਿੱਚੋਂ %1$d ਕਾਰਜ ਪੜਾਅ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ਅੱਗੇ ਜਾਣ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"ਕਿਰਪਾ ਕਰਕੇ ਵੱਖ-ਵੱਖ ਟੈਮਪਲੇਟਾਂ \'ਤੇ ਜਾਓ ਅਤੇ ਪੱਕਾ ਕਰੋ ਕਿ ਕਾਰ ਡਰਾਈਵਿੰਗ ਮੋਡ ਵਿੱਚ ਹੈ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ਟੌਗਲ ਬਟਨ ਦਾ ਡੈਮੋ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ਟੈਸਟ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"ਸਪਸ਼ਟ ਤਬਦੀਲੀਆਂ ਕਰਨ ਦੀ ਆਗਿਆ ਹੈ"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"ਪਿਛਲੇ ਪੰਨੇ \'ਤੇ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਆਗਿਆ ਹੈ।"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"\'ਟੈਸਟ ਨੂੰ ਟੌਗਲ ਕਰੋ\' ਚਾਲੂ ਹੈ"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"\'ਟੈਸਟ ਨੂੰ ਟੌਗਲ ਕਰੋ\' ਬੰਦ ਹੈ"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਅਤੇ ਸਜਾਵਟ ਦਾ ਡੈਮੋ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਜਾਂਚ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ਸਿਰਫ਼ ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਨੂੰ ਹੀ ਚੁਣਿਆ ਜਾ ਸਕਦਾ ਹੈ"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ਸਜਾਵਟ ਜਾਂਚ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈਆਂ ਅਤੇ ਸਜਾਵਟ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ਕਤਾਰ ਨੂੰ ਵੀ ਚੁਣਿਆ ਜਾ ਸਕਦਾ ਹੈ"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਚੁਣੀ ਗਈ ਹੈ"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"ਕਤਾਰ ਨਾਲ ਸੰਬੰਧਿਤ ਪ੍ਰਾਇਮਰੀ ਕਾਰਵਾਈ ਚੁਣੀ ਗਈ ਹੈ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ਫੁਟਕਲ ਟੈਮਪਲੇਟਾਂ ਦੇ ਡੈਮੋ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ਸ਼ੋਅਕੇਸ ਦੇ ਡੈਮੋ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ਟੈਮਪਲੇਟ ਖਾਕੇ ਦੇ ਡੈਮੋ"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"ਅਵਾਜ਼ੀ ਪਹੁੰਚ ਡੈਮੋ ਸਕ੍ਰੀਨ"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"ਵਰਤੋਂਕਾਰ ਦੀਆਂ ਅੰਤਰਕਿਰਿਆਵਾਂ"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ਇਜਾਜ਼ਤਾਂ ਦੀ ਬੇਨਤੀ ਦਾ ਡੈਮੋ"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ਐਪਲੀਕੇਸ਼ਨ ਓਵਰਫ਼ਲੋ ਵੈਲੀਡੇਟਰ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ਕਿਰਪਾ ਕਰਕੇ ਵਾਹਨ ਨੂੰ ਪਾਰਕ ਤੋਂ ਡਰਾਈਵਿੰਗ ਸਥਿਤੀ ਵਿੱਚ ਬਦਲਦੇ ਸਮੇਂ"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ਅੱਗੇ ਦਿੱਤੇ ਟੈਮਪਲੇਟਾਂ ਨੂੰ ਅਜ਼ਮਾਓ"</string>
     <string name="perm_group" msgid="3834918337351876270">"ਇਜਾਜ਼ਤ ਗਰੁੱਪ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ਸ਼ੋਅਕੇਸ ਐਪ ਲਈ ਇਜਾਜ਼ਤ ਗਰੁੱਪ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
index a355377..bb11ae2 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Osiągnięto limit zadań\nKontynuowanie spowoduje wymuszenie zatrzymania aplikacji"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Krok zadania %1$d z %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknij, aby przejść dalej"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Sprawdź różne szablony i upewnij się, że samochód jest w trybie jazdy"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Wersja demonstracyjna przycisku przełączania"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test przełączania"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Zmiany stanu są dozwolone"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Aktualizacje dozwolone na operacjach cofania."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test przełączania został włączony"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test przełączania został wyłączony"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Działanie alternatywne i wersja demonstracyjna dekoracji"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test działania alternatywnego"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Można wybrać tylko działanie alternatywne"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test dekoracji"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Działania alternatywne i dekoracja"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Można wybrać również wiersz"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Wybrano działanie alternatywne"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Wybrano wiersz z działaniem głównym"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Wersje demonstracyjne różnych szablonów"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Wersje demonstracyjne do prezentacji"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Wersje demonstracyjne szablonów układu"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ekran wersji demonstracyjnej Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interakcje użytkownika"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Wersje demonstracyjne próśb o uprawnienia"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Walidator przepełnienia aplikacji"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Przetestuj te szablony, gdy przełączasz"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"samochód ze stanu zaparkowania do stanu jazdy"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupa uprawnień"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupa uprawnień dotyczących aplikacji Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Dostęp do dokładnej lokalizacji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
index e06208c..3ab19fd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite da tarefa atingido\nContinuar vai forçar a parada do app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Etapa da tarefa: %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Clique para continuar"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e confira se o veículo está no modo carro"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstração de botão de ativação"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Alternar teste"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Mudanças com estado são permitidas"</string>
@@ -307,35 +306,24 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Atualizações permitidas em operações de retorno."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Alternância de teste ativada"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Alternância de teste desativada"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstração de ação secundária e de decoração"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Teste de ação secundária"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Apenas a ação secundária pode ser selecionada"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Teste de decoração"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Ações secundárias e decoração"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"A linha também pode ser selecionada"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"A ação secundária está selecionada"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"A ação principal da linha está selecionada"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstração de modelos diversos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações em destaque"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações de modelos de layout"</string>
     <string name="grid_template_menu_demo_title" msgid="7096285873490705119">"Demonstrações de modelo de grade"</string>
-    <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Acesso por voz"</string>
+    <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interações do usuário"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Solicitar demonstração de permissões"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador do menu flutuante do aplicativo"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Teste os seguintes modelos durante a mudança"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"o veículo do estado de estacionamento para o de condução"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupo de permissões"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupo de permissões do app Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Acesso à localização precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
index de619c2..90157c4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"O limite de tarefas foi atingido\nSe avançar, vai forçar a paragem da app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Passo da tarefa %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Clique para avançar"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e certifique-se de que o carro está no modo de condução"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstração do botão ativar/desativar"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Ativar/desativar teste"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"São permitidas alterações com estado"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ecrã de demonstração do Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interações do utilizador"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstrações sobre como pedir autorizações"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador de menu adicional da aplicação"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Teste os seguintes modelos durante o carregamento"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"o veículo de estacionado para o estado de condução"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupo de autorizações"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupo de autorizações da app Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Acesso à localização exata"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
index e06208c..3ab19fd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite da tarefa atingido\nContinuar vai forçar a parada do app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Etapa da tarefa: %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Clique para continuar"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e confira se o veículo está no modo carro"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstração de botão de ativação"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Alternar teste"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Mudanças com estado são permitidas"</string>
@@ -307,35 +306,24 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Atualizações permitidas em operações de retorno."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Alternância de teste ativada"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Alternância de teste desativada"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstração de ação secundária e de decoração"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Teste de ação secundária"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Apenas a ação secundária pode ser selecionada"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Teste de decoração"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Ações secundárias e decoração"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"A linha também pode ser selecionada"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"A ação secundária está selecionada"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"A ação principal da linha está selecionada"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstração de modelos diversos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações em destaque"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações de modelos de layout"</string>
     <string name="grid_template_menu_demo_title" msgid="7096285873490705119">"Demonstrações de modelo de grade"</string>
-    <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Acesso por voz"</string>
+    <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interações do usuário"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Solicitar demonstração de permissões"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador do menu flutuante do aplicativo"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Teste os seguintes modelos durante a mudança"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"o veículo do estado de estacionamento para o de condução"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupo de permissões"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupo de permissões do app Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Acesso à localização precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
index fb087ba..8e0262c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"A fost atinsă limita pentru activități\nDacă alegi să continui, aplicația se va opri forțat"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Pasul activității: %1$d din %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Dă clic pentru a merge înainte"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Accesează diferitele șabloane și asigură-te că mașina este în modul Cu mașina"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstrație cu buton de comutare"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Activează / dezactivează testul"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Modificările cu menținere de stare sunt permise"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Sunt permise actualizările operațiunilor în fundal."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Testul de comutare este activat"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Testul de comutare este dezactivat"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstrație pentru acțiunea secundară și decorație"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test pentru acțiunea secundară"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Numai acțiunea secundară poate fi selectată"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test pentru decorație"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Acțiuni secundare și decorație"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Și rândul poate fi selectat"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Acțiunea secundară a fost selectată"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Acțiunea principală de pe rând a fost selectată"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrații diverse cu șabloane"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrații pentru Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrații cu aspecte de șablon"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ecran demonstrativ pentru accesul vocal"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interacțiunile utilizatorilor"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Solicită demonstrații privind permisiunile"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Program de validare a meniului suplimentar al aplicației"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testează următoarele șabloane în timpul schimbării"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"stării vehiculului din Parcat în Cu mașina"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grup de permisiuni"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grup de permisiuni pentru aplicația Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Acces la locația fină"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
index 940b3c6..8bd8eff 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнут предел по числу задач.\nЕсли продолжить, работа приложения будет остановлена."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Шаг задачи: %1$d из %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Нажмите, чтобы продолжить"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Посмотрите разные шаблоны и убедитесь, что автомобиль находится в режиме вождения"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Переключатель вида \"включено/отключено\" (режим демонстрации)"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Проверка переключения"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Изменения с отслеживанием состояния разрешены"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Обновления разрешены для обратных операций."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Проверка переключения включена"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Проверка переключения отключена"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрация второстепенного действия и оформления"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тест второстепенного действия"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Можно выбрать только второстепенное действие"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Тест оформления"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Второстепенные действия и оформление"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Можно также выбрать строку"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Выбрано второстепенное действие."</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Выбрано главное действие для строки."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрации прочих шаблонов"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрации Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Макет шаблона (режим демонстрации)"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Экран демонстрации Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Взаимодействие пользователя"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрации запроса разрешений"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Проверка дополнительного меню приложения"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Протестируйте следующие шаблоны при переходе"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"транспортного средства из режима парковки в режим вождения"</string>
     <string name="perm_group" msgid="3834918337351876270">"Группа разрешений"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Группа разрешений для приложения \"Витрина\""</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Доступ к данным о точном местоположении"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
index 612fcd6..8ffa434 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"කාර්ය සීමාව ළඟා විය\nඉදිරියට යාම යෙදුම බලෙන් නවත්වනු ඇත"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d කින් %1$dවන කාර්ය පියවර"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ඉදිරියට යාමට ක්ලික් කරන්න"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"විවිධ අච්චු වෙත පැමිණ මෝටර් රථය ධාවන ප්‍රකාරයේ ඇති බව තහවුරු කර ගන්න"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"බොත්තම් ආදර්ශනය ටොගල් කරන්න"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ටොගල පරීක්ෂාව"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"රාජ්‍ය වෙනස් කිරීම්වලට ඉඩ දේ"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"පසු මෙහෙයුම්වල යාවත්කාලීන කිරීම් ඉඩ දෙනු ලැබේ."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"ටොගල පරීක්ෂාව සබලයි"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"ටොගල පරීක්ෂාව අබලයි"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ද්විතීයික ක්‍රියා සහ සැරසිලි ආදර්ශනය"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ද්විතියික ක්‍රියා පරීක්ෂණය"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ද්විතියික ක්‍රියාව පමණක් තෝරා ගත හැක"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"සැරසිලි පරීක්ෂණය"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ද්විතියික ක්‍රියා සහ සැරසිලි"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"පේළිය ද තෝරා ගත හැක"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ද්විතියික ක්‍රියාව තෝරා ඇත"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"පේළියේ මූලික ක්‍රියාව තෝරා ඇත"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"විවිධ අච්චු ආදර්ශන"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ප්‍රකාශක තේරූ ආදර්ශන"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"අච්චු පිරිසැලසුම් ආදර්ශන"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"හඬ ප්‍රවේශ ආදර්ශන තිරය"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"පරිශීලක අන්තර්ක්‍රියා"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"අවසර ආදර්ශන ඉල්ලන්න"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"යෙදුම් ඉතිරියෑම් වලංගුකරණය"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"වෙනස් කරන අතරතුර පහත සඳහන් අච්චු පරීක්ෂා කරන්න"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"වාහනය ගාල් කළ තත්ත්වයේ සිට ධාවන තත්ත්වයට"</string>
     <string name="perm_group" msgid="3834918337351876270">"අවසර සමූහය"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ප්‍රදර්ශන යෙදුම සඳහා අවසර සමූහය"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"නිවැරදි ස්ථානයට ප්‍රවේශය"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
index e09222bf..d039c09 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Bolo dosiahnuté obmedzenie úlohy\nAk budete pokračovať, aplikácia sa vynútene zastaví"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Krok úlohy: %1$d z %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Pokračujte kliknutím"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Prejdite na rôzne šablóny a uistite sa, že auto je režime v aute"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo prepínača"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test prepínača"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Zmeny so stavom sú povolené"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Aktualizácie sú povolené pre operácie na pozadí."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test prepínača bol aktivovaný"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test prepínača bol deaktivovaný"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekundárna akcia a demo dekorácie"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundárnej akcie"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Vybrať sa dá iba sekundárna akcia"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Test dekorácie"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundárne akcie a dekorácia"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Môžete vybrať aj daný riadok"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Bola vybraná sekundárna akcia"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Bola vybraná primárna akcia riadka"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Rôzne šablóny – demá"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Výber – demá"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo rozložení šablóny"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Obrazovka demo verzie aplikácie Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interakcie používateľov"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Vyžadovať demá povolení"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Nástroj na overenie nadbytku aplikácií"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Otestujte tieto šablóny pri zmene"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"vozidla zo zaparkovaného stavu na stav jazdy"</string>
     <string name="perm_group" msgid="3834918337351876270">"Skupina povolení"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Skupina povolení pre aplikáciu Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Prístup k presnej polohe"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
index c210245..8a2083c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dosežena je omejitev opravil.\nČe nadaljujete, bo aplikacija prisilno ustavljena."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Korak opravila %1$d od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite, če želite nadaljevati."</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Odprite različne predloge in poskrbite, da je avtomobil v načinu vožnje."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Predstavitvena različica preklopnega gumba"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Preizkus preklopa"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Spremembe stanj so dovoljene."</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Posodobitve so dovoljene pri pomikih nazaj."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Preizkus preklopa je omogočen."</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Preizkus preklopa je onemogočen."</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Predstavitvena različica sekundarnega dejanja in okrasitve"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Preizkus sekundarnega dejanja"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Izbrati je mogoče samo sekundarno dejanje."</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Preizkus okrasitve"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundarna dejanja in okrasitve"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Izbrati je mogoče tudi vrstico."</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundarno dejanje je izbrano."</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Glavno dejanje vrstice je izbrano."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Predstavitvene različice različnih predlog"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Predstavitvene različice izpostavljenih stvari"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Predstavitvene različice postavitev predlog"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Predstavitvena različica zaslona za glasovni dostop"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Uporabniške interakcije"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Predstavitvene različice zahtev za dovoljenja"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Orodje za preverjanje presežkov aplikacije"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Preizkusite te predloge,"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ko preklapljate vozilo iz stanja parkiranosti v stanje vožnje."</string>
     <string name="perm_group" msgid="3834918337351876270">"Skupina dovoljenj"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Skupina dovoljenj za aplikacijo Predstavitev"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Dostop do natančne lokacije"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
index 4e5475c..588afb4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"U arrit kufiri i detyrës\nVazhdimi përpara do ta ndalojë aplikacionin me forcë"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Hapi i detyrës: %1$d nga %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliko për të vazhduar përpara"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Vizito shabllonet e ndryshme dhe sigurohu që makina të jetë në modalitetin e lëvizjes"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstrimi i butonit të aktivizimit/çaktivizimit"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Testimi i butonit aktivizo/çaktivizo"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Lejohen ndryshimet në monitorim"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Përditësimet lejohen në veprimet e mëparshme."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Testimi i butonit aktivizo/çaktivizo u aktivizua"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Testimi i butonit aktivizo/çaktivizo u çaktivizua"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Veprimi dytësor dhe demonstrimi i dekorimit"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Testimi i veprimit dytësor"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Mund të zgjidhet vetëm veprimi dytësor"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Testimi i dekorimit"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Veprimet dytësore dhe dekorimi"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Rreshti mund të zgjidhet gjithashtu"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"U zgjodh veprimi dytësor"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"U zgjodh veprimi kryesor i rreshtit"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrime shabllonesh të ndryshme"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrime të prezantimit"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrimet e strukturës së shabllonit"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Ekrani demonstrues i \"Qasjes me zë\""</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Ndërveprimet e përdoruesit"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstrimet e kërkesës për leje"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Verifikuesi i tejkalimit të aplikacionit"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testo shabllonet e mëposhtme gjatë ndryshimit"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"të automjetit nga gjendja e parkimit në gjendjen e lëvizjes"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupi i lejeve"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupi i lejeve për aplikacionin Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Qasje në vendndodhjen e saktë"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
index ec69183..c0d4d7f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнуто је ограничење задатака\nАко наставите, апликација ће се принудно зауставити"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d. корак задатка од %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Кликните да бисте ишли напред"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Посетите различите шаблоне и уверите се да је аутомобил у режиму вожње"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Демонстрација дугмета за укључивање/искључивање"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Укључи/искључи тест"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Промене стања су дозвољене"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Ажурирања су дозвољена за операције у позадини."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Тест укључивања/искључивања је омогућен"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Тест укључивања/искључивања је онемогућен"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрација секундарне радње и декорације"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тест секундарне радње"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Може да се изабере само секундарна радња"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Тест декорације"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Секундарне радње и декорација"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Може да се изабере и ред"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Секундарна радња је изабрана"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Примарна радња реда је изабрана"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрације различитих шаблона"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрације приказивања"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрације изгледа шаблона"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Екран демонстрације приступа гласу"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Корисничке интеракције"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрације захтева за дозволе"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Валидатор прекорачења апликације"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Тестирајте следеће шаблоне током пуњења"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"статус возила из паркираног у вожња"</string>
     <string name="perm_group" msgid="3834918337351876270">"Група дозвола"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Група дозвола за истицање апликације"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Приступ прецизној локацији"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
index 135c174..8aa06dc 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Gränsen för antal uppgifter har uppnåtts\nOm du fortsätter tvingas appen att avslutas"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Uppgiftssteg %1$d av %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klicka för att gå vidare"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Öppna de olika mallarna och se till att bilen är i körläge"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo för på/av-knapp"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Test för att aktivera och inaktivera"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Tillståndsbaserade ändringar är tillåtna"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Uppdateringar tillåtet i bakåtåtgärder."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Test av aktivera/inaktivera har aktiverats"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Test av aktivera/inaktivera har inaktiverats"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo av den sekundära åtgärden och dekoration"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test av den sekundära åtgärden"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Det går endast att välja den sekundära åtgärden"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Dekorationstest"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundära åtgärder och dekoration"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Det går även att välja raden"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Den sekundära åtgärden har valts"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Radens primära åtgärd har valts"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demor för övriga mallar"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Visa demor"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demor för mallayouter"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Demoskärm för röststyrning"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Användarinteraktioner"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demor av behörighetsförfrågningar"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validering av appspill"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testa följande mallar när du byter"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"från parkeringsläge till körläge i fordonet"</string>
     <string name="perm_group" msgid="3834918337351876270">"Behörighetsgrupp"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Behörighetsgrupp för exempelapp"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Åtkomst till exakt plats"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
index d49aa0a..e9b8e2f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Kikomo cha shughuli kimefikiwa\nKuendelea kutalazimisha kuzima programu"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Hatua ya shughuli %1$d kati ya %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Bofya ili uendelee"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Tafadhali tembelea violezo tofauti na uhakikishe kuwa gari liko katika hali ya kuendesha gari"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Onyesho la Kitufe cha Kugeuza"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Jaribio la kipengele cha kuwasha/kuzima"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Mabadiliko yanayoweza kurudiwa mara kwa mara yanaruhusiwa"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Masasisho yameruhusiwa kwenye shughuli za nyuma."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Jaribio la Kipengele cha Kuwasha/Kuzima Limewashwa"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Jaribio la Kipengele cha Kuwasha/Kuzima Limezimwa"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Onyesho la Kitendo cha Upili na Jaribio la Usanifu"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Jaribio la Kitendo cha Upili"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Kitendo cha upili tu ndiyo kinaweza kuchaguliwa"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Jaribio la Usanifu"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Matendo ya Upili na Usanifu"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Safu mlalo pia inaweza kuchaguliwa"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Kitendo cha Upili kimechaguliwa"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Kitendo cha msingi cha safu mlalo kimechaguliwa"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Maonyesho ya Violezo Anuwai"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Maonyesho ya Kuangazia"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Maonyesho ya Kiolezo cha Muundo"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Skrini ya Toleo la Kujaribu la Kufikia kwa Kutamka"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Shughuli za Mtumiaji"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Omba Matoleo ya Kujaribu ya Ruhusa"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Kithibitishaji cha Vipengee vya ziada vya Programu"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Tafadhali jaribu violezo vifuatavyo huku ukibadilisha"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"gari limeondolewa katika hali ya kuegeshwa na kuwekwa katika hali ya kuendesha"</string>
     <string name="perm_group" msgid="3834918337351876270">"Kikundi cha Ruhusa"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Kikundi cha Ruhusa cha Programu ya Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Ufikiaji wa Eneo Mahususi"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
index 801c17c..81fa068 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"பணி வரம்பை எட்டிவிட்டது.\nமேலும் தொடர்ந்தால் ஆப்ஸ் உடனே நிறுத்தப்படும்"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d / %2$d படியை எடுங்கள்"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"முன்னேறிச் செல்ல கிளிக் செய்யவும்"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"வெவ்வேறு டெம்ப்ளேட்களைப் பாருங்கள். கார் \'வாகனம் ஓட்டும் பயன்முறையில்\' இருப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"நிலைமாற்றும் பட்டனின் டெமோ"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"நிலைமாற்றப் பரிசோதனை"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"நிலை மாற்றங்கள் அனுமதிக்கப்படுகின்றன"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"முந்தைய பக்கத்திற்குச் சென்று மாற்றங்களைச் செய்யலாம்."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"நிலைமாற்றப் பரிசோதனை இயக்கப்பட்டது"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"நிலைமாற்றப் பரிசோதனை முடக்கப்பட்டது"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"இரண்டாம்நிலைச் செயல் மற்றும் அலங்காரத்திற்கான டெமோ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"இரண்டாம்நிலைச் செயலுக்கான பரிசோதனை"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"இரண்டாம்நிலைச் செயலை மட்டுமே தேர்ந்தெடுக்க முடியும்"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"அலங்காரப் பரிசோதனை"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"இரண்டாம்நிலைச் செயல்கள் மற்றும் அலங்காரம்"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"வரிசையையும் தேர்ந்தெடுக்க முடியும்"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"இரண்டாம்நிலைச் செயல் தேர்ந்தெடுக்கப்பட்டது"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"வரிசையின் முதன்மைச் செயல் தேர்ந்தெடுக்கப்பட்டது"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"மற்ற டெம்ப்ளேட்டுகளின் டெமோக்கள்"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ஷோகேஸின் டெமோக்கள்"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"டெம்ப்ளேட் தளவமைப்பின் டெமோக்கள்"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"குரல் அணுகல் டெமோ திரை"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"பயனர் செயல்பாடுகள்"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"அனுமதிகள் டெமோக்களைக் கோருங்கள்"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ஆப்ஸ் ஓவர்ஃப்லோ பரிசோதிப்பான்"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"மாற்றும்போது பின்வரும் டெம்ப்ளேட்களைப் பரிசோதித்துப் பாருங்கள்"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"வாகனத்தின் நிலையைப் பார்க்கிங்கில் இருந்து இயக்கத்திற்கு மாற்றுங்கள்"</string>
     <string name="perm_group" msgid="3834918337351876270">"அனுமதிக் குழு"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"ஷோகேஸ் ஆப்ஸிற்கான அனுமதிக் குழு"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"சரியான இருப்பிடத்திற்கான அணுகல்"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
index c440829..556538c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"టాస్క్‌ల పరిమితిని చేరుకున్నారు\nఇంకా టాస్క్‌లను ఎంచుకుంటే యాప్ ఫోర్స్ స్టాప్ అవుతుంది"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$dలో %1$d టాస్క్ దశ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ముందుకు వెళ్లడానికి క్లిక్ చేయండి"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"దయచేసి వివిధ టెంప్లేట్‌లను సందర్శించండి, కారు డ్రైవింగ్ మోడ్‌లో ఉందని నిర్ధారించుకోండి"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"టోగుల్ బటన్ డెమో"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"టోగుల్ టెస్ట్"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"స్టేట్‌ఫుల్ మార్పులు అనుమతించబడతాయి"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"బ్యాక్‌గ్రౌండ్ ఆపరేషన్‌ల కోసం అప్‌డేట్‌లు అనుమతించబడ్డాయి."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"టోగుల్ టెస్ట్ ఎనేబుల్ చేయబడింది"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"టోగుల్ టెస్ట్ డిజేబుల్ చేయబడింది"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"రెండవ చర్య, డెకరేషన్ డెమో"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"రెండవ చర్య పరీక్ష"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"రెండవ చర్యను మాత్రమే ఎంచుకోవచ్చు"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"డెకరేషన్ టెస్ట్"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"రెండవ చర్య, డెకరేషన్"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"అడ్డు వరుసను కూడా ఎంచుకోవచ్చు"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"రెండవ చర్య ఎంచుకోబడింది"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"అడ్డు వరుస ప్రాథమిక చర్య ఎంచుకోబడింది"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ఇతర టెంప్లేట్‌ల డెమోలు"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"డెమోలను ప్రదర్శించండి"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"టెంప్లేట్ లేఅవుట్ డెమోలు"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access డెమో స్క్రీన్"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"యూజర్ ఇంటరాక్షన్‌లు"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"అనుమతుల డెమోలను రిక్వెస్ట్ చేయండి"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"అప్లికేషన్ ఓవర్‌ఫ్లో వాలిడేటర్"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"దయచేసి మార్చేటప్పుడు కింది టెంప్లేట్‌లను పరీక్షించండి"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"వాహనం పార్క్ నుండి డ్రైవింగ్ స్టేట్‌కు మారండి"</string>
     <string name="perm_group" msgid="3834918337351876270">"అనుమతి గ్రూప్"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Showcase యాప్ కోసం అనుమతి గ్రూప్"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"ఖచ్చితమైన లొకేషన్ యాక్సెస్"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
index 409e74f..e6fcd8f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ถึงขีดจำกัดงานแล้ว\nการดำเนินการต่อจะบังคับให้แอปหยุด"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"งานขั้นตอนที่ %1$d จาก %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"คลิกเพื่อดำเนินการต่อ"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"โปรดไปที่เทมเพลตอื่นและตรวจสอบว่ารถอยู่ในโหมดขับรถ"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"การสาธิตปุ่มเปิด/ปิด"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"การทดสอบการสลับ"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"อนุญาตการเปลี่ยนแปลงแบบเก็บสถานะ"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"หน้าจอสาธิตการเข้าถึงด้วยเสียง"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"การโต้ตอบของผู้ใช้"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"การสาธิตการขอสิทธิ์"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"โปรแกรมตรวจสอบรายการเพิ่มเติมของแอปพลิเคชัน"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"โปรดทดสอบเทมเพลตต่อไปนี้ขณะเปลี่ยน"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"สถานะยานพาหนะจากจอดอยู่เป็นกำลังขับ"</string>
     <string name="perm_group" msgid="3834918337351876270">"กลุ่มสิทธิ์"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"กลุ่มสิทธิ์สำหรับแอป Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"เข้าถึงตำแหน่งอย่างละเอียด"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
index bcc8aba..554ce6c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Naabot na ang limitasyon ng gawain\nSapilitang ihihinto ang app kapag nagpatuloy"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Gawin ang hakbang %1$d sa %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Mag-click para magpatuloy"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Pakibisita ang iba\'t ibang template at tiyaking nasa driving mode ang kotse"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo ng Toggle Button"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"I-toggle ang test"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Pinapayagan ang mga pagbabago sa status"</string>
@@ -322,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Screen ng Demo ng Pag-access gamit ang Boses"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Mga Pakikipag-ugnayan ng User"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Humiling ng Mga Demo ng Pahintulot"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Validator ng Overflow ng Application"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Pakisubukan ang mga sumusunod na template habang binabago"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ang status ng sasakyan mula naka-park patungong nagmamaneho"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grupo ng Pahintulot"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Grupo ng Pahintulot para sa Showcase App"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Access sa Eksaktong Lokasyon"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
index 5dc17de..28e0de1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Görev sınırına ulaşıldı\nDevam ederseniz uygulama zorla durdurulacak"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d/%2$d görev adımı"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Devam etmek için tıklayın"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Lütfen farklı şablonları inceleyip arabanın sürüş modunda olduğundan emin olun"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Açma/Kapatma Düğmesi Demosu"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Açma/kapatma testi"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Durum bilgili değişikliklere izin verilir"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Önceki işlemlerde güncellemelere izin verilir."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Açma/Kapatma Testi Etkin"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Açma/Kapatma Testi Devre Dışı"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"İkincil İşlem ve Süsleme Demosu"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"İkincil İşlem Testi"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Yalnızca ikincil işlem seçilebilir"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Süsleme Testi"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"İkincil İşlemler ve Süsleme"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Satır da seçilebilir"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"İkincil işlem seçildi"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Satırdaki birincil işlem seçildi"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Çeşitli Şablon Demoları"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Öne Çıkan Demoları"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Şablon Düzeni Demoları"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Sesli Erişim Demo Ekranı"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Kullanıcı Etkileşimleri"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"İzin İsteme Demoları"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Uygulama Taşma Doğrulayıcı"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Park halindeki aracı sürüş durumuna geçirirken"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"lütfen aşağıdaki şablonları test edin"</string>
     <string name="perm_group" msgid="3834918337351876270">"İzin Grubu"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Vitrin Uygulamasının İzin Grubu"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Hassas Konuma Erişim"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
index 1b79a87..937028f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Досягнуто ліміту завдань\nЯкщо продовжити, програму буде примусово зупинено"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Крок завдання %1$d з %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Натисніть, щоб продовжити"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Перегляньте різні шаблони й переконайтеся, що автомобіль перебуває в режимі кермування"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Демонстрація перемикача"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Змінити перевірку"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Дозволено змінювати стан"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Оновлення дозволено для операцій повернення."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Перевірку перемикача ввімкнено"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Перевірку перемикача вимкнено"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрація додаткової дії й оформлення"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Перевірка додаткової дії"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Можна вибрати лише додаткову дію"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Перевірка оформлення"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Додаткові дії й оформлення"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Рядок також можна вибрати"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Вибрано додаткову дію"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Вибрано основну дію рядка"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрації інших шаблонів"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Увімкнути демонстрації"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрації макетів шаблонів"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Демонстрація екрана Голосового доступу"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Взаємодія з користувачем"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрації запитів на отримання дозволів"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Перевірка додаткового меню додатка"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Перевірте наведені далі шаблони,"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"коли змінюватимете режим паркування на кермування автомобілем"</string>
     <string name="perm_group" msgid="3834918337351876270">"Група дозволів"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Група дозволів для демонстраційного додатка"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Доступ до даних про точне місцезнаходження"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
index 9a3b163..0608c26 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
@@ -296,8 +296,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"ٹاسک کی حد پوری ہو گئی\nآگے بڑھنے سے ایپ کو زبردستی روک دیا جائے گا"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"‏%2$d میں سے ‎%1$d ٹاسک کا مرحلہ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"آگے بڑھنے کے لیے کلک کریں"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"براہ کرم مختلف ٹیمپلیٹس دیکھیں اور یقینی بنائیں کہ کار ڈرائیونگ موڈ میں ہے"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"ٹوگل بٹن کا ڈیمو"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"ٹیسٹ کو ٹوگل کریں"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"اسٹیٹ فل تبدیلیوں کی اجازت ہے"</string>
@@ -311,22 +310,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"اپ ڈیٹس پچھلے صفحے پر جانے کی اجازت دی گئی۔"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"ٹیسٹ ٹوگل کرنے کی سہولت فعال ہے"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"ٹیسٹ ٹوگل کرنے کی سہولت غیر فعال ہے"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ثانوی کارروائی اور ڈیکوریشن ڈیمو"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"ثانوی کارروائی ٹیسٹ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"صرف ثانوی کارروائی کا انتخاب کیا جا سکتا ہے"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"ڈیکوریشن ٹیسٹ"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ثانوی کارروائیاں اور ڈیکوریشن"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"قطار بھی منتخب کی جا سکتی ہے"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"ثانوی کارروائی کا انتخاب کیا گیا ہے"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"قطار کی بنیادی کارروائی کا انتخاب کیا گیا ہے"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"متفرق تمثیلات کے ڈیموز"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ڈیموز دکھائیں"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"تمثیل کے لے آؤٹ کے ڈیموز"</string>
@@ -334,12 +325,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"صوتی رسائی کی ڈیمو سکرین"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"صارف کے تعاملات"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"اجازتوں کے ڈیموز کی درخواست کریں"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"ایپلیکیشن اوور فلو کا تصدیق کنندہ"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"براہ کرم تبدیل کرتے وقت درج ذیل ٹیمپلیٹس کی جانچ کریں"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"گاڑی پارک کرنے سے ڈرائیونگ کی حالت تک"</string>
     <string name="perm_group" msgid="3834918337351876270">"اجازت کا گروپ"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"شوکیس ایپ کے لیے اجازت کا گروپ"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"درست مقام تک رسائی"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
index 5c718b9..b2801a5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Vazifa chekloviga yetildi\nDavom etish ilovani majburiy toʻxtatadi."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Vazifa qadami: %1$d / %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Oldinga oʻtish uchun bosing"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Turli andozalarni ochib koʻring va avtomobil haydash rejimida ekanini tekshiring"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo ekran tugmasi"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Almashtirish tekshiruvi"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Vaziyatni oʻzgartirishga ruxsat beriladi"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Teskari amallarda yangilanishlarga ruxsat berilgan."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Testni oʻchirish/yoqish faollashtirildi"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Testni oʻchirish/yoqish faolsizlantirildi"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Ikkilamchi amal va bezak demosi"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Ikkilamchi amal testi"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Faqat ikkilamchi amal tanlanishi mumkin"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Bezak testi"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Ikkilamchi amallar va bezak"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Qator ham tanlanishi mumkin"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Ikkilamchi amal tanlandi"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Asosiy amal qatori tanlandi"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Aralash andozalar demolari"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Namoyish demolari"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Andoza dizayni demolari"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Demo ekranida ovozli boshqaruv"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Foydalanuvchi harakatlari"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demolarga ruxsatlarni talab qilish"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Ilovani chuqur tekshirish vositasi"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Tahrirlash vaqtida quyidagi andozalarni sinang"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"parklangan avtomobil rejimidan haydash rejimiga"</string>
     <string name="perm_group" msgid="3834918337351876270">"Ruxsatlar guruhi"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Vitrina ilovasi uchun ruxsatlar guruhi"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Aniq joylashuv axborotiga ruxsat"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
index 807875a1..2bd3d63 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Đã đạt đến giới hạn nhiệm vụ\nNếu tiếp tục sẽ buộc phải dừng ứng dụng"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Bước %1$d trong số %2$d của nhiệm vụ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Nhấp để tiếp tục"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Vui lòng truy cập nhiều mẫu riêng biệt và đảm bảo xe đang ở chế độ lái"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Bản minh hoạ nút bật tắt"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Kiểm tra chuyển đổi"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Các thay đổi rõ ràng được phép"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Cho phép cập nhật đối với các thiết bị chạy ngầm."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Đã bật tính năng chuyển đổi"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Đã tắt tính năng kiểm tra chuyển đổi"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Bản minh hoạ phần trang trí và hành động phụ"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Thử nghiệm hành động phụ"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Bạn chỉ có thể chọn hành động phụ"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Thử nghiệm phần trang trí"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Phần trang trí và hành động phụ"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Bạn cũng có thể chọn hàng"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Đã chọn hành động phụ"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Đã chọn hành động chính cho hàng"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Bản demo biểu mẫu Misc"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Bản demo nổi bật"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Bản minh hoạ bố cục mẫu"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Màn hình minh hoạ chức năng Điều khiển bằng giọng nói"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Hoạt động tương tác của người dùng"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Bản minh hoạ về yêu cầu quyền"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Trình xác thực chế độ kết xuất bổ sung của ứng dụng"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Vui lòng kiểm tra các mẫu sau trong khi thay đổi"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"chiếc xe từ trạng thái đỗ sang trạng thái lái"</string>
     <string name="perm_group" msgid="3834918337351876270">"Nhóm quyền"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Nhóm quyền dành cho ứng dụng Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Truy cập vào thông tin vị trí chính xác"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
index db8258f..5c20e7e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"已达到任务限制\n继续会强制停止应用"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"任务第 %1$d 步(共 %2$d 步)"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"点击以继续"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"请访问不同模板并确保此车辆处于驾驶模式"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"切换按钮演示"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"切换测试"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"允许有状态更改"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"返回操作允许更新。"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"切换测试已启用"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"切换测试已停用"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"次要操作和装饰演示"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"次要操作测试"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"只能选择次要操作"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"装饰测试"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"次要操作和装饰"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"也可以选择行"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"次要操作处于选中状态"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"行主要操作处于选中状态"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"其他模板演示"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"展示演示"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"模板布局演示"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access 演示屏幕"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"用户互动"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"请求权限演示"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"应用溢出验证器"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"更改时请测试以下模板"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"从停车状态变为驾驶状态的车辆"</string>
     <string name="perm_group" msgid="3834918337351876270">"权限组"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"“展示”应用的权限组"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"访问精确位置信息"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
index d62fa2d..b5b7c32 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"工作已達上限\n繼續使用將強制停止應用程式"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"點擊即可繼續使用"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"請查看各種範本,並確保汽車處於駕駛模式"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"「切換按鈕」示範"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"切換測試"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"允許有狀態的變更"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"允許在返回時更新。"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"已啟用切換測試"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"已停用切換測試"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"次要動作及裝飾示範"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"次要動作測試"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"只可選取次要動作"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"裝飾測試"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"次要動作及裝飾"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"亦可選取列"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"已選取次要動作"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"已選取列的主要動作"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"其他範本示範"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"展示示範"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"「範本版面配置」示範"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"語音操控示範畫面"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"使用者互動"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"「要求權限」示範"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"應用程式展開式驗證工具"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"請在變更時測試以下範本"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"從泊車狀態轉為行駛狀態的汽車"</string>
     <string name="perm_group" msgid="3834918337351876270">"權限群組"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"「展示」應用程式的權限群組"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"存取精確位置"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
index 5fed181..8e2921b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"工作已達上限\n繼續使用將強制停止應用程式"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"點選即可繼續使用"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"請前往不同範本並確認車輛處於行車模式"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"「切換鈕」示範"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"切換測試"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"允許有狀態的變更"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"允許返回上一頁更新。"</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"切換測試已啟用"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"切換測試已停用"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"次要動作和裝飾示範"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"次要動作測試"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"只可以選取次要動作"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"裝飾測試"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"次要動作與裝飾"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"也可以選取列"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"已選取次要動作"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"已選取列的主要動作"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"其他範本示範"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"「展示」示範"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"「範本版面配置」示範"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access 示範畫面"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"使用者互動"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"要求權限示範"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"應用程式溢位驗證工具"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"請在變更時測試以下範本"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"從停車狀態轉為行駛狀態的車輛"</string>
     <string name="perm_group" msgid="3834918337351876270">"權限群組"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"「展示」應用程式的權限群組"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"精確位置存取權"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
index 2adeb6d..45c51e3 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
@@ -292,8 +292,7 @@
     <string name="task_limit_reached_msg" msgid="6038763366777119364">"Umkhawulo womsebenzi ufinyelelwe\nUkuqhubekela phambili kuzophoqa ukumisa i-app"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Isinyathelo somsebenzi %1$d kwengu-%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Chofoza ukuze uye phambili"</string>
-    <!-- no translation found for task_content_allowed (545061986612431190) -->
-    <skip />
+    <string name="task_content_allowed" msgid="545061986612431190">"Sicela uvakashele izifanekiso ezahlukene futhi uqinisekise ukuthi imoto ikumodi yokushayela"</string>
     <string name="toggle_button_demo_title" msgid="3179103600967398928">"Inkinobho Yokuguqula I-demo"</string>
     <string name="toggle_test_title" msgid="924485265152862631">"Guqula ukuhlola"</string>
     <string name="toggle_test_text" msgid="8107217216013312857">"Izinguquko ezisemthethweni zivunyelwe"</string>
@@ -307,22 +306,14 @@
     <string name="additional_data_text" msgid="2846223398214158872">"Izibuyekezo zivunyelwe ukusebenza ngemuva."</string>
     <string name="toggle_test_enabled" msgid="982370904182034076">"Ukuhlola Kokuguqula Kunikwe Amandla"</string>
     <string name="toggle_test_disabled" msgid="8366040658408451664">"Ukuhlola Kokuguqula Kukhutshaziwe"</string>
-    <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
-    <skip />
-    <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
-    <skip />
-    <!-- no translation found for decoration_test_title (8450127046762442244) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
-    <skip />
-    <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
-    <skip />
-    <!-- no translation found for secondary_action_toast (5076434693504006565) -->
-    <skip />
-    <!-- no translation found for row_primary_action_toast (756516694751965204) -->
-    <skip />
+    <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Idemo Yesibili Yesenzo Nokuhlobisa"</string>
+    <string name="secondary_actions_test_title" msgid="3664453747553733613">"Ukuhlolwa Kwesenzo Kwesibili"</string>
+    <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Isenzo sesibili kuphela esingakhethwa"</string>
+    <string name="decoration_test_title" msgid="8450127046762442244">"Ukuhlolwa Kokuhlobisa"</string>
+    <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Izenzo Zesibili Nokuhlobisa"</string>
+    <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Umugqa ungabuye ukhethwe"</string>
+    <string name="secondary_action_toast" msgid="5076434693504006565">"Isenzo Sesibili Sikhethiwe"</string>
+    <string name="row_primary_action_toast" msgid="756516694751965204">"Isenzo esiyinhloko somugqa sikhethiwe"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Amademo Ezifanekiso Ezixubile"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Bonisa Amademo"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Ama-demo Esakhiwo Sesifanekiso"</string>
@@ -330,12 +321,9 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Isikrini Sedemo Sokufinyelela Ngezwi"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Ukusebenzisana Komsebenzisi"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Cela Amademo Ezimvume"</string>
-    <!-- no translation found for application_overflow_title (396427940886169325) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
-    <skip />
-    <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
-    <skip />
+    <string name="application_overflow_title" msgid="396427940886169325">"Isiqinisekisi sokuchichima se-app"</string>
+    <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Sicela uhlole izifanekiso ezilandelayo ngenkathi ushintsha"</string>
+    <string name="application_overflow_subtitle2" msgid="4385123036846369714">"imoto isuka lapho imile iye esimweni sokushayela"</string>
     <string name="perm_group" msgid="3834918337351876270">"Iqembu Lemvume"</string>
     <string name="perm_group_description" msgid="7348847631139139024">"Iqembu Lemvume Le-app Ye-showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Ukufinyelela Endaweni Enhle"</string>
diff --git a/collection/collection-benchmark/build.gradle b/collection/collection-benchmark/build.gradle
index 1cec99e..122d25a 100644
--- a/collection/collection-benchmark/build.gradle
+++ b/collection/collection-benchmark/build.gradle
@@ -106,7 +106,7 @@
 
 androidx {
     name = "AndroidX Collections Benchmarks (Android / iOS)"
-    mavenGroup = LibraryGroups.COLLECTIONS
+    mavenGroup = LibraryGroups.COLLECTION
     inceptionYear = "2022"
     description = "AndroidX Collections Benchmarks (Android / iOS)"
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt
index ea32dce..cef6022 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt
@@ -33,10 +33,8 @@
 import org.jetbrains.kotlin.cli.common.messages.MessageCollector
 import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
 import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
 import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
-import org.jetbrains.kotlin.codegen.ClassBuilderFactories
 import org.jetbrains.kotlin.codegen.ClassFileFactory
 import org.jetbrains.kotlin.codegen.GeneratedClassLoader
 import org.jetbrains.kotlin.config.CommonConfigurationKeys
@@ -172,10 +170,7 @@
             try {
                 val environment = myEnvironment ?: error("Environment not initialized")
                 val files = myFiles ?: error("Files not initialized")
-                val generationState = GenerationUtils.compileFiles(
-                    files.psiFiles, environment, ClassBuilderFactories.TEST,
-                    NoScopeRecordCliBindingTrace()
-                )
+                val generationState = GenerationUtils.compileFiles(environment, files.psiFiles)
                 generationState.factory.also { classFileFactory = it }
             } catch (e: TestsCompilerError) {
                 if (reportProblems) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt
index cfc467a..8806902 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt
@@ -16,15 +16,11 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
-import org.jetbrains.kotlin.checkers.DiagnosedRange
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
-import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
-import org.jetbrains.kotlin.config.JVMConfigurationKeys
-import org.jetbrains.kotlin.config.JvmTarget
-import org.jetbrains.kotlin.diagnostics.Diagnostic
 import java.io.File
+import org.jetbrains.kotlin.checkers.DiagnosedRange
+import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.diagnostics.Diagnostic
 
 abstract class AbstractComposeDiagnosticsTest : AbstractCompilerTest() {
 
@@ -40,16 +36,7 @@
         val files = listOf(file)
 
         // Use the JVM version of the analyzer to allow using classes in .jar files
-        val moduleTrace = NoScopeRecordCliBindingTrace()
-        val result = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
-            environment.project,
-            files,
-            moduleTrace,
-            environment.configuration.copy().apply {
-                this.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
-            },
-            environment::createPackagePartProvider
-        )
+        val result = JvmResolveUtil.analyze(environment, files)
 
         // Collect the errors
         val errors = result.bindingContext.diagnostics.all().toMutableList()
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index 7fa4afd..594bc5d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -343,7 +343,7 @@
         ComposeComponentRegistrar.registerCommonExtensions(environment.project)
         IrGenerationExtension.registerExtension(environment.project, extension)
 
-        val analysisResult = JvmResolveUtil.analyze(files, environment)
+        val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(environment, files)
         val codegenFactory = JvmIrCodegenFactory(
             configuration,
             configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases)
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallResolverTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallResolverTests.kt
index e52cc06..9b45bd8 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallResolverTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallResolverTests.kt
@@ -221,9 +221,9 @@
         val environment = myEnvironment ?: error("Environment not initialized")
 
         val ktFile = KtPsiFactory(environment.project).createFile(text)
-        val bindingContext = JvmResolveUtil.analyze(
-            ktFile,
-            environment
+        val bindingContext = JvmResolveUtil.analyzeAndCheckForErrors(
+            environment,
+            listOf(ktFile)
         ).bindingContext
 
         carets.forEachIndexed { index, (offset, calltype) ->
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/GenerationUtils.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/GenerationUtils.kt
index 0df3b5e..d77cf5d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/GenerationUtils.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/GenerationUtils.kt
@@ -16,80 +16,41 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import com.intellij.psi.search.GlobalSearchScope
 import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
 import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
 import org.jetbrains.kotlin.backend.jvm.jvmPhases
 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
 import org.jetbrains.kotlin.codegen.ClassBuilderFactories
-import org.jetbrains.kotlin.codegen.ClassBuilderFactory
-import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
 import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
 import org.jetbrains.kotlin.codegen.state.GenerationState
-import org.jetbrains.kotlin.config.CompilerConfiguration
-import org.jetbrains.kotlin.config.JVMConfigurationKeys
-import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.resolve.AnalyzingUtils
-import org.jetbrains.kotlin.resolve.BindingTrace
 
 object GenerationUtils {
-    @JvmStatic
-    @JvmOverloads
     fun compileFiles(
-        files: List<KtFile>,
         environment: KotlinCoreEnvironment,
-        classBuilderFactory: ClassBuilderFactory = ClassBuilderFactories.TEST,
-        trace: BindingTrace = NoScopeRecordCliBindingTrace()
-    ): GenerationState =
-        compileFiles(
-            files,
-            environment.configuration,
-            classBuilderFactory,
-            environment::createPackagePartProvider,
-            trace
-        )
-
-    @JvmStatic
-    @JvmOverloads
-    fun compileFiles(
         files: List<KtFile>,
-        configuration: CompilerConfiguration,
-        classBuilderFactory: ClassBuilderFactory,
-        packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
-        trace: BindingTrace = NoScopeRecordCliBindingTrace()
     ): GenerationState {
-        val analysisResult =
-            JvmResolveUtil.analyzeAndCheckForErrors(
-                files.first().project,
-                files,
-                configuration,
-                packagePartProvider,
-                trace
-            )
+        val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(environment, files)
         analysisResult.throwIfError()
 
         val state = GenerationState.Builder(
-            files.first().project,
-            classBuilderFactory,
+            environment.project,
+            ClassBuilderFactories.TEST,
             analysisResult.moduleDescriptor,
             analysisResult.bindingContext,
             files,
-            configuration
+            environment.configuration
         ).codegenFactory(
-            if (configuration.getBoolean(JVMConfigurationKeys.IR))
-                JvmIrCodegenFactory(
-                    configuration,
-                    configuration.get(CLIConfigurationKeys.PHASE_CONFIG)
-                        ?: PhaseConfig(jvmPhases)
-                )
-            else DefaultCodegenFactory
-        ).build()
-        if (analysisResult.shouldGenerateCode) {
-            KotlinCodegenFacade.compileCorrectFiles(state)
-        }
+            JvmIrCodegenFactory(
+                environment.configuration,
+                environment.configuration.get(CLIConfigurationKeys.PHASE_CONFIG)
+                    ?: PhaseConfig(jvmPhases)
+            )
+        ).isIrBackend(true).build()
+
+        KotlinCodegenFacade.compileCorrectFiles(state)
 
         // For JVM-specific errors
         try {
@@ -100,4 +61,4 @@
 
         return state
     }
-}
\ No newline at end of file
+}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/JvmResolveUtil.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/JvmResolveUtil.kt
index adadec6..629bfca 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/JvmResolveUtil.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/JvmResolveUtil.kt
@@ -16,26 +16,17 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import com.intellij.openapi.project.Project
-import com.intellij.psi.search.GlobalSearchScope
 import org.jetbrains.kotlin.analyzer.AnalysisResult
-import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
 import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
-import org.jetbrains.kotlin.config.CompilerConfiguration
-import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.resolve.AnalyzingUtils
-import org.jetbrains.kotlin.resolve.BindingTrace
 
 object JvmResolveUtil {
-    @JvmStatic
     fun analyzeAndCheckForErrors(
-        project: Project,
-        files: Collection<KtFile>,
-        configuration: CompilerConfiguration,
-        packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
-        trace: BindingTrace = CliBindingTrace()
+        environment: KotlinCoreEnvironment,
+        files: Collection<KtFile>
     ): AnalysisResult {
         for (file in files) {
             try {
@@ -45,13 +36,7 @@
             }
         }
 
-        return analyze(
-            project,
-            files,
-            configuration,
-            packagePartProvider,
-            trace
-        ).apply {
+        return analyze(environment, files).apply {
             try {
                 AnalyzingUtils.throwExceptionOnErrors(bindingContext)
             } catch (e: Exception) {
@@ -60,40 +45,12 @@
         }
     }
 
-    @JvmStatic
-    fun analyze(file: KtFile, environment: KotlinCoreEnvironment): AnalysisResult =
-        analyze(setOf(file), environment)
-
-    @JvmStatic
-    fun analyze(files: Collection<KtFile>, environment: KotlinCoreEnvironment): AnalysisResult =
-        analyze(
-            files,
-            environment,
-            environment.configuration
-        )
-
-    @JvmStatic
-    fun analyze(
-        files: Collection<KtFile>,
-        environment: KotlinCoreEnvironment,
-        configuration: CompilerConfiguration
-    ): AnalysisResult =
-        analyze(
+    fun analyze(environment: KotlinCoreEnvironment, files: Collection<KtFile>): AnalysisResult =
+        TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
             environment.project,
             files,
-            configuration,
+            NoScopeRecordCliBindingTrace(),
+            environment.configuration,
             environment::createPackagePartProvider
         )
-
-    private fun analyze(
-        project: Project,
-        files: Collection<KtFile>,
-        configuration: CompilerConfiguration,
-        packagePartProviderFactory: (GlobalSearchScope) -> PackagePartProvider,
-        trace: BindingTrace = CliBindingTrace()
-    ): AnalysisResult {
-        return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
-            project, files, trace, configuration, packagePartProviderFactory
-        )
-    }
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ScopeComposabilityTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ScopeComposabilityTests.kt
index e201d0a..5f00b26 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ScopeComposabilityTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ScopeComposabilityTests.kt
@@ -152,10 +152,8 @@
 
         val ktFile = KtPsiFactory(environment.project).createFile(text)
         val bindingContext = JvmResolveUtil.analyzeAndCheckForErrors(
-            environment.project,
+            environment,
             listOf(ktFile),
-            environment.configuration,
-            environment::createPackagePartProvider
         ).bindingContext
 
         carets.forEachIndexed { index, (offset, marking) ->
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt
index fc6c275..2f620f9 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt
@@ -178,7 +178,7 @@
             interface Bar {
                 @Composable
                 fun composableFunction(param: Boolean): Boolean
-                val composableProperty: Boolean @Composable get()
+                @get:Composable val composableProperty: Boolean
                 fun nonComposableFunction(param: Boolean): Boolean
                 val nonComposableProperty: Boolean
             }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
index 9d9672b..92acae1 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
@@ -1569,6 +1569,110 @@
         }
     }
 
+    @Test
+    fun animatingItemsWithPreviousIndexLargerThanTheNewItemCount() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7))
+        val gridSize = itemSize * 2 - 1
+        rule.setContent {
+            LazyGrid(2, maxSize = with(rule.density) { gridSize.toDp() }) {
+                items(list, key = { it }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertLayoutInfoPositions(
+            0 to AxisIntOffset(0, 0),
+            1 to AxisIntOffset(itemSize, 0),
+            2 to AxisIntOffset(0, itemSize),
+            3 to AxisIntOffset(itemSize, itemSize)
+        )
+
+        rule.runOnIdle {
+            list = listOf(0, 2, 4, 6)
+        }
+
+        onAnimationFrame { fraction ->
+            val expected = mutableListOf<Pair<Any, IntOffset>>().apply {
+                add(0 to AxisIntOffset(0, 0))
+                add(
+                    2 to AxisIntOffset(
+                        (itemSize * fraction).roundToInt(),
+                        (itemSize * (1f - fraction)).roundToInt()
+                    )
+                )
+                    val item4MainAxis = itemSize + (itemSize * (1f - fraction)).roundToInt()
+                if (item4MainAxis < gridSize) {
+                    add(
+                        4 to AxisIntOffset(0, item4MainAxis)
+                    )
+                }
+                val item6MainAxis = itemSize + (itemSize * 2 * (1f - fraction)).roundToInt()
+                if (item6MainAxis < gridSize) {
+                    add(
+                        6 to AxisIntOffset(itemSize, item6MainAxis)
+                    )
+                }
+            }
+
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
+    @Test
+    fun animatingItemsWithPreviousIndexLargerThanTheNewItemCount_differentSpans() {
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6))
+        val gridSize = itemSize * 2 - 1
+        rule.setContent {
+            LazyGrid(2, maxSize = with(rule.density) { gridSize.toDp() }) {
+                items(list, key = { it }, span = {
+                    GridItemSpan(if (it == 6) maxLineSpan else 1)
+                }) {
+                    Item(it)
+                }
+            }
+        }
+
+        assertLayoutInfoPositions(
+            0 to AxisIntOffset(0, 0),
+            1 to AxisIntOffset(itemSize, 0),
+            2 to AxisIntOffset(0, itemSize),
+            3 to AxisIntOffset(itemSize, itemSize)
+        )
+
+        rule.runOnIdle {
+            list = listOf(0, 4, 6)
+        }
+
+        onAnimationFrame { fraction ->
+            val expected = mutableListOf<Pair<Any, IntOffset>>().apply {
+                add(0 to AxisIntOffset(0, 0))
+                val item4MainAxis = (itemSize * 2 * (1f - fraction)).roundToInt()
+                if (item4MainAxis < gridSize) {
+                    add(
+                        4 to AxisIntOffset(itemSize, item4MainAxis)
+                    )
+                }
+                if (fraction == 1f) {
+                    val item6MainAxis = itemSize + (itemSize * 2 * (1f - fraction)).roundToInt()
+                    if (item6MainAxis < gridSize) {
+                        add(
+                            6 to AxisIntOffset(0, item6MainAxis)
+                        )
+                    }
+                }
+            }
+
+            assertPositions(
+                expected = expected.toTypedArray(),
+                fraction = fraction
+            )
+        }
+    }
+
     private fun AxisIntOffset(crossAxis: Int, mainAxis: Int) =
         if (isVertical) IntOffset(crossAxis, mainAxis) else IntOffset(mainAxis, crossAxis)
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt
new file mode 100644
index 0000000..042769b
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2022 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.foundation.text
+
+import android.os.Build
+import androidx.compose.foundation.text.matchers.assertThat
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shadow
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@MediumTest
+class DrawPhaseAttributesToggleTest(private val config: Config) {
+
+    private val textTag = "text"
+
+    class Config(
+        private val description: String,
+        val updateStyle: (TextStyle) -> TextStyle,
+        val initializeStyle: (TextStyle) -> TextStyle = { it }
+    ) {
+        override fun toString(): String = "toggling $description"
+    }
+
+    @OptIn(ExperimentalTextApi::class)
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = arrayOf(
+            Config(
+                "color unspecified/color/unspecified",
+                initializeStyle = { it.copy(color = Color.Unspecified) },
+                updateStyle = { it.copy(color = Color.Blue) },
+            ),
+            Config(
+                "color colorA/colorB/colorA",
+                initializeStyle = { it.copy(color = Color.Black) },
+                updateStyle = { it.copy(color = Color.Blue) },
+            ),
+            Config(
+                "color colorA/brushA/colorA",
+                initializeStyle = {
+                    it.copy(color = Color.Red)
+                },
+                updateStyle = {
+                    it.copy(brush = Brush.verticalGradient(listOf(Color.Blue, Color.Magenta)))
+                }
+            ),
+            Config(
+                "brush brushA/brushB/brushA",
+                initializeStyle = {
+                    it.copy(brush = Brush.horizontalGradient(listOf(Color.Black, Color.Blue)))
+                },
+                updateStyle = {
+                    it.copy(brush = Brush.verticalGradient(listOf(Color.Red, Color.Blue)))
+                }
+            ),
+            Config(
+                "brush brushA/colorA/brushA",
+                initializeStyle = {
+                    it.copy(brush = Brush.horizontalGradient(listOf(Color.Black, Color.Blue)))
+                },
+                updateStyle = {
+                    it.copy(color = Color.Red)
+                }
+            ),
+            Config(
+                "alpha",
+                initializeStyle = {
+                    it.copy(
+                        alpha = 1f,
+                        brush = Brush.verticalGradient(0f to Color.Blue, 1f to Color.Magenta)
+                    )
+                },
+                updateStyle = { it.copy(alpha = 0.5f, brush = it.brush) },
+            ),
+            Config(
+                "textDecoration none/lineThrough/none",
+                initializeStyle = { it.copy(textDecoration = TextDecoration.None) },
+                updateStyle = { it.copy(textDecoration = TextDecoration.LineThrough) }
+            ),
+            Config(
+                "textDecoration lineThrough/none/lineThrough",
+                initializeStyle = { it.copy(textDecoration = TextDecoration.LineThrough) },
+                updateStyle = { it.copy(textDecoration = TextDecoration.None) }
+            ),
+            Config(
+                "textDecoration null/lineThrough/null",
+                initializeStyle = { it.copy(textDecoration = null) },
+                updateStyle = { it.copy(textDecoration = TextDecoration.LineThrough) }
+            ),
+            Config(
+                "shadow null/shadow/null",
+                initializeStyle = { it.copy(shadow = null) },
+                updateStyle = { it.copy(shadow = Shadow(Color.Black, blurRadius = 4f)) }
+            ),
+            Config(
+                "shadow shadowA/shadowB/shadowA",
+                initializeStyle = { it.copy(shadow = Shadow(Color.Black, blurRadius = 1f)) },
+                updateStyle = { it.copy(shadow = Shadow(Color.Black, blurRadius = 4f)) }
+            ),
+            Config(
+                "shadow shadowA/null/shadowA",
+                initializeStyle = { it.copy(shadow = Shadow(Color.Black, blurRadius = 1f)) },
+                updateStyle = { it.copy(shadow = null) }
+            ),
+            Config(
+                "drawStyle null/drawStyle/null",
+                initializeStyle = { it.copy(drawStyle = null) },
+                updateStyle = { it.copy(drawStyle = Stroke(width = 2f)) }
+            ),
+            Config(
+                "drawStyle drawStyleA/drawStyleB/drawStyleA",
+                initializeStyle = { it.copy(drawStyle = Stroke(width = 1f)) },
+                updateStyle = { it.copy(drawStyle = Stroke(width = 2f)) }
+            ),
+            Config(
+                "drawStyle drawStyle/null/drawStyle",
+                initializeStyle = { it.copy(drawStyle = Stroke(width = 1f)) },
+                updateStyle = { it.copy(drawStyle = null) }
+            ),
+            Config(
+                "drawStyle stroke/fill/stroke",
+                initializeStyle = { it.copy(drawStyle = Stroke(width = 1f)) },
+                updateStyle = { it.copy(drawStyle = Fill) }
+            )
+        )
+    }
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun basicText() {
+        var style by mutableStateOf(
+            TextStyle(
+                color = Color.Black,
+                textDecoration = null,
+                shadow = null
+            ).let(config.initializeStyle)
+        )
+
+        rule.setContent {
+            BasicText(
+                "ABC",
+                style = style,
+                modifier = Modifier.testTag(textTag)
+            )
+        }
+
+        rule.waitForIdle()
+        val initialBitmap = rule.onNodeWithTag(textTag).captureToImage().asAndroidBitmap()
+
+        style = config.updateStyle(style)
+
+        rule.waitForIdle()
+        val updatedBitmap = rule.onNodeWithTag(textTag).captureToImage().asAndroidBitmap()
+        assertThat(initialBitmap).isNotEqualToBitmap(updatedBitmap)
+
+        style = config.initializeStyle(style)
+
+        rule.waitForIdle()
+        val finalBitmap = rule.onNodeWithTag(textTag).captureToImage().asAndroidBitmap()
+        assertThat(finalBitmap).isNotEqualToBitmap(updatedBitmap)
+
+        assertThat(finalBitmap).isEqualToBitmap(initialBitmap)
+    }
+
+    @Test
+    fun basicTextField() {
+        var style by mutableStateOf(config.initializeStyle(TextStyle(color = Color.Black)))
+
+        rule.setContent {
+            BasicTextField(
+                "ABC",
+                >
+                textStyle = style,
+                modifier = Modifier.testTag(textTag)
+            )
+        }
+
+        rule.waitForIdle()
+        val initialBitmap = rule.onNodeWithTag(textTag).captureToImage().asAndroidBitmap()
+
+        style = config.updateStyle(style)
+
+        rule.waitForIdle()
+        val updatedBitmap = rule.onNodeWithTag(textTag).captureToImage().asAndroidBitmap()
+        assertThat(initialBitmap).isNotEqualToBitmap(updatedBitmap)
+
+        style = config.initializeStyle(style)
+
+        rule.waitForIdle()
+        val finalBitmap = rule.onNodeWithTag(textTag).captureToImage().asAndroidBitmap()
+        assertThat(finalBitmap).isNotEqualToBitmap(updatedBitmap)
+
+        assertThat(finalBitmap).isEqualToBitmap(initialBitmap)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index aab5ed0..7aac595 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -350,6 +350,9 @@
 
     fun Offset.reverseIfNeeded(): Offset = if (reverseDirection) this * -1f else this
 
+    /**
+     * @return the amount of scroll that was consumed
+     */
     fun ScrollScope.dispatchScroll(availableDelta: Offset, source: NestedScrollSource): Offset {
         val scrollDelta = availableDelta.singleAxisOffset()
         val overscrollPreConsumed = overscrollPreConsumeDelta(scrollDelta, source)
@@ -376,7 +379,7 @@
             source
         )
 
-        return leftForParent - parentConsumed
+        return overscrollPreConsumed + preConsumedByParent + axisConsumed + parentConsumed
     }
 
     fun overscrollPreConsumeDelta(
@@ -447,8 +450,7 @@
         var result: Velocity = available
         scrollableState.scroll {
             val outerScopeScroll: (Offset) -> Offset = { delta ->
-                val consumed = this.dispatchScroll(delta.reverseIfNeeded(), Fling)
-                delta - consumed.reverseIfNeeded()
+                dispatchScroll(delta.reverseIfNeeded(), Fling).reverseIfNeeded()
             }
             val scope = object : ScrollScope {
                 override fun scrollBy(pixels: Float): Float {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
index 78679fe..351a8a1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
@@ -22,6 +22,7 @@
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.VisibilityThreshold
 import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -361,12 +362,9 @@
             if (!reverseLayout) viewportStartItemIndex > index else viewportStartItemIndex < index
         return when {
             afterViewportEnd -> {
-                val fromIndex = if (!reverseLayout) {
-                    // viewportEndItemIndex is the last item in the line already
-                    viewportEndItemIndex + 1
-                } else {
-                    spanLayoutProvider.firstIndexInNextLineAfter(index)
-                }
+                val fromIndex = spanLayoutProvider.firstIndexInNextLineAfter(
+                    if (!reverseLayout) viewportEndItemIndex else index
+                )
                 val toIndex = spanLayoutProvider.lastIndexInPreviousLineBefore(
                     if (!reverseLayout) index else viewportEndItemIndex
                 )
@@ -383,12 +381,9 @@
                 val fromIndex = spanLayoutProvider.firstIndexInNextLineAfter(
                     if (!reverseLayout) index else viewportStartItemIndex
                 )
-                val toIndex = if (!reverseLayout) {
-                    // viewportStartItemIndex is the first item in the line already
-                    viewportStartItemIndex - 1
-                } else {
-                    spanLayoutProvider.lastIndexInPreviousLineBefore(index)
-                }
+                val toIndex = spanLayoutProvider.lastIndexInPreviousLineBefore(
+                    if (!reverseLayout) viewportStartItemIndex else index
+                )
                 viewportStartItemNotVisiblePartSize + scrolledBy.mainAxis +
                     // minus the size of this item as we are looking for the start offset of it.
                     -mainAxisSizeWithSpacings +
@@ -485,18 +480,6 @@
     visibilityThreshold = IntOffset.VisibilityThreshold
 )
 
-private fun LazyGridSpanLayoutProvider.lastIndexInPreviousLineBefore(index: Int): Int {
-    val lineIndex = getLineIndexOfItem(index)
-    val lineConfiguration = getLineConfiguration(lineIndex.value)
-    return lineConfiguration.firstItemIndex - 1
-}
-
-private fun LazyGridSpanLayoutProvider.firstIndexInNextLineAfter(index: Int): Int {
-    val lineIndex = getLineIndexOfItem(index)
-    val lineConfiguration = getLineConfiguration(lineIndex.value)
-    return lineConfiguration.firstItemIndex + lineConfiguration.spans.size
-}
-
 private fun LazyGridSpanLayoutProvider.getLinesMainAxisSizesSum(
     fromIndex: Int,
     toIndex: Int,
@@ -532,3 +515,45 @@
     }
     return fallback
 }
+
+private fun LazyGridSpanLayoutProvider.lastIndexInPreviousLineBefore(index: Int) =
+    firstIndexInLineContaining(index) - 1
+
+private fun LazyGridSpanLayoutProvider.firstIndexInNextLineAfter(index: Int) =
+    if (index >= totalSize) {
+        // after totalSize we just approximate with 1 slot per item
+        firstIndexInLineContaining(index) + slotsPerLine
+    } else {
+        val lineIndex = getLineIndexOfItem(index)
+        val lineConfiguration = getLineConfiguration(lineIndex.value)
+        lineConfiguration.firstItemIndex + lineConfiguration.spans.size
+    }
+
+private fun LazyGridSpanLayoutProvider.firstIndexInLineContaining(index: Int): Int {
+    return if (index >= totalSize) {
+        val firstIndexForLastKnowLine = getFirstIndexInNextLineAfterTheLastKnownOne()
+        // after totalSize we just approximate with 1 slot per item
+        val linesBetween = (index - firstIndexForLastKnowLine) / slotsPerLine
+        firstIndexForLastKnowLine + slotsPerLine * linesBetween
+    } else {
+        val lineIndex = getLineIndexOfItem(index)
+        val lineConfiguration = getLineConfiguration(lineIndex.value)
+        lineConfiguration.firstItemIndex
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun LazyGridSpanLayoutProvider.getFirstIndexInNextLineAfterTheLastKnownOne(): Int {
+    // first we find the line for the `totalSize - 1` item
+    val lineConfiguration = getLineConfiguration(getLineIndexOfItem(totalSize - 1).value)
+    var currentSpan = 0
+    var currentIndex = lineConfiguration.firstItemIndex - 1
+    // then we go through all the known spans
+    lineConfiguration.spans.fastForEach {
+        currentSpan += it.currentLineSpan
+        currentIndex++
+    }
+    // and increment index as if we had more items with slot == 1 until we switch to the next line
+    currentIndex += slotsPerLine - currentSpan + 1
+    return currentIndex
+}
diff --git a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt
index be6e663..4e62a3d 100644
--- a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt
+++ b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt
@@ -119,6 +119,10 @@
     val sourceSet = project.getMultiplatformSourceSet(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
     val generatedSrcMainDirectory = buildDirectory.resolve(IconGenerationTask.GeneratedSrcMain)
     sourceSet.kotlin.srcDir(project.files(generatedSrcMainDirectory).builtBy(task))
+    // add it to the multiplatform sources as well.
+    project.tasks.named("multiplatformSourceJar", Jar::class.java).configure {
+        it.from(task.map { generatedSrcMainDirectory })
+    }
     project.addToSourceJar(generatedSrcMainDirectory, task)
 }
 
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index bc57514..f96b55a 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -103,6 +103,9 @@
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
+  public final class CalendarModelKt {
+  }
+
   @androidx.compose.runtime.Immutable public final class CardColors {
   }
 
@@ -681,6 +684,9 @@
 
 package androidx.compose.material3.internal {
 
+  public final class AndroidDatePickerModel_androidKt {
+  }
+
   public final class ExposedDropdownMenuPopupKt {
   }
 
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 906e393..c815b0b 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -132,6 +132,9 @@
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
+  public final class CalendarModelKt {
+  }
+
   @androidx.compose.runtime.Immutable public final class CardColors {
   }
 
@@ -1013,6 +1016,9 @@
 
 package androidx.compose.material3.internal {
 
+  public final class AndroidDatePickerModel_androidKt {
+  }
+
   public final class ExposedDropdownMenuPopupKt {
   }
 
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index bc57514..f96b55a 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -103,6 +103,9 @@
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
+  public final class CalendarModelKt {
+  }
+
   @androidx.compose.runtime.Immutable public final class CardColors {
   }
 
@@ -681,6 +684,9 @@
 
 package androidx.compose.material3.internal {
 
+  public final class AndroidDatePickerModel_androidKt {
+  }
+
   public final class ExposedDropdownMenuPopupKt {
   }
 
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CalendarModelTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CalendarModelTest.kt
new file mode 100644
index 0000000..9b5997e
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CalendarModelTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 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.material3
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.material3.internal.CalendarModelImpl
+import androidx.compose.material3.internal.LegacyCalendarModelImpl
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import java.util.Locale
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@MediumTest
+@RunWith(Parameterized::class)
+@OptIn(ExperimentalMaterial3Api::class)
+@RequiresApi(Build.VERSION_CODES.O)
+internal class CalendarModelTest(private val model: CalendarModel) {
+
+    @Test
+    fun dateCreation() {
+        val date = model.getDate(January2022Millis) // 1/1/2022
+        assertThat(date.year).isEqualTo(2022)
+        assertThat(date.month).isEqualTo(1)
+        assertThat(date.dayOfMonth).isEqualTo(1)
+        assertThat(date.utcTimeMillis).isEqualTo(January2022Millis)
+    }
+
+    @Test
+    fun dateRestore() {
+        val date =
+            CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+        assertThat(model.getDate(date.utcTimeMillis)).isEqualTo(date)
+    }
+
+    @Test
+    fun monthCreation() {
+        val date =
+            CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+        val monthFromDate = model.getMonth(date)
+        val monthFromMilli = model.getMonth(January2022Millis)
+        val monthFromYearMonth = model.getMonth(year = 2022, month = 1)
+        assertThat(monthFromDate).isEqualTo(monthFromMilli)
+        assertThat(monthFromDate).isEqualTo(monthFromYearMonth)
+    }
+
+    @Test
+    fun monthRestore() {
+        val month = model.getMonth(year = 1999, month = 12)
+        assertThat(model.getMonth(month.startUtcTimeMillis)).isEqualTo(month)
+    }
+
+    @Test
+    fun plusMinusMonth() {
+        val month = model.getMonth(January2022Millis) // 1/1/2022
+        val expectedNextMonth = model.getMonth(month.endUtcTimeMillis + 1) // 2/1/2022
+        val plusMonth = model.plusMonths(from = month, addedMonthsCount = 1)
+        assertThat(plusMonth).isEqualTo(expectedNextMonth)
+        assertThat(model.minusMonths(from = plusMonth, subtractedMonthsCount = 1)).isEqualTo(month)
+    }
+
+    @Test
+    fun parseDate() {
+        val expectedDate =
+            CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+        val parsedDate = model.parse("1/1/2022", "M/d/yyyy")
+        assertThat(parsedDate).isEqualTo(expectedDate)
+    }
+
+    @Test
+    fun formatDate() {
+        val date =
+            CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+        val month = model.plusMonths(model.getMonth(date), 2)
+        assertThat(model.format(date, "MM/dd/yyyy")).isEqualTo("01/01/2022")
+        assertThat(model.format(month, "MM/dd/yyyy")).isEqualTo("03/01/2022")
+    }
+
+    @Test
+    fun weekdayNames() {
+        // Ensure we are running on a US locale for this test.
+        Locale.setDefault(Locale.US)
+        val weekDays = model.weekdayNames
+        assertThat(weekDays).hasSize(DaysInWeek)
+        // Check that the first day is always "Monday", per ISO-8601 standard.
+        assertThat(weekDays.first().first).ignoringCase().contains("Monday")
+        weekDays.forEach {
+            assertThat(it.second.first().lowercaseChar()).isEqualTo(
+                it.first.first().lowercaseChar()
+            )
+        }
+    }
+
+    @Test
+    fun equalModelsOutput() {
+        // Note: This test ignores the parameters and just runs a few equality tests for the output.
+        // It will execute twice, but that should to tolerable :)
+        val newModel = CalendarModelImpl()
+        val legacyModel = LegacyCalendarModelImpl()
+
+        val date = newModel.getDate(January2022Millis) // 1/1/2022
+        val legacyDate = legacyModel.getDate(January2022Millis)
+        val month = newModel.getMonth(date)
+        val legacyMonth = legacyModel.getMonth(date)
+
+        assertThat(newModel.today).isEqualTo(legacyModel.today)
+        assertThat(month).isEqualTo(legacyMonth)
+        assertThat(newModel.plusMonths(month, 3)).isEqualTo(legacyModel.plusMonths(month, 3))
+        assertThat(date).isEqualTo(legacyDate)
+        assertThat(newModel.getDayOfWeek(date)).isEqualTo(legacyModel.getDayOfWeek(date))
+        assertThat(newModel.format(date, "MMM d, yyyy")).isEqualTo(
+            legacyModel.format(
+                date,
+                "MMM d, yyyy"
+            )
+        )
+        assertThat(newModel.format(month, "MMM yyyy")).isEqualTo(
+            legacyModel.format(
+                month,
+                "MMM yyyy"
+            )
+        )
+    }
+
+    internal companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = arrayOf(
+            CalendarModelImpl(),
+            LegacyCalendarModelImpl()
+        )
+    }
+}
+
+private const val January2022Millis = 1640995200000
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AndroidDatePickerModel.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AndroidDatePickerModel.android.kt
new file mode 100644
index 0000000..089aeb0
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AndroidDatePickerModel.android.kt
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2022 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.material3.internal
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.material3.CalendarDate
+import androidx.compose.material3.CalendarModel
+import androidx.compose.material3.CalendarMonth
+import androidx.compose.material3.DaysInWeek
+import androidx.compose.material3.ExperimentalMaterial3Api
+import java.text.DateFormatSymbols
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.time.DayOfWeek
+import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeParseException
+import java.time.format.TextStyle
+import java.time.temporal.WeekFields
+import java.util.Calendar
+import java.util.Locale
+import java.util.TimeZone
+
+/**
+ * Creates a [CalendarModel] to be used by the date picker.
+ */
+@ExperimentalMaterial3Api
+internal fun createDefaultCalendarModel(): CalendarModel {
+    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+        CalendarModelImpl()
+    } else {
+        LegacyCalendarModelImpl()
+    }
+}
+
+/**
+ * A [CalendarModel] implementation for API < 26.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+internal class LegacyCalendarModelImpl : CalendarModel {
+
+    override val today
+        get(): CalendarDate {
+            val systemCalendar = Calendar.getInstance()
+            systemCalendar[Calendar.HOUR_OF_DAY] = 0
+            systemCalendar[Calendar.MINUTE] = 0
+            systemCalendar[Calendar.SECOND] = 0
+            systemCalendar[Calendar.MILLISECOND] = 0
+            val utcOffset =
+                systemCalendar.get(Calendar.ZONE_OFFSET) + systemCalendar.get(Calendar.DST_OFFSET)
+            return CalendarDate(
+                year = systemCalendar[Calendar.YEAR],
+                month = systemCalendar[Calendar.MONTH] + 1,
+                dayOfMonth = systemCalendar[Calendar.DAY_OF_MONTH],
+                utcTimeMillis = systemCalendar.timeInMillis + utcOffset
+            )
+        }
+
+    override val firstDayOfWeek: Int = dayInISO8601(Calendar.getInstance().firstDayOfWeek)
+
+    override val weekdayNames: List<Pair<String, String>> = buildList {
+        val weekdays = DateFormatSymbols(Locale.getDefault()).weekdays
+        val shortWeekdays = DateFormatSymbols(Locale.getDefault()).shortWeekdays
+        // Skip the first item, as it's empty, and the second item, as it represents Sunday while it
+        // should be last according to ISO-8601.
+        weekdays.drop(2).forEachIndexed { index, day ->
+            add(Pair(day, shortWeekdays[index + 2]))
+        }
+        // Add Sunday to the end.
+        add(Pair(weekdays[1], shortWeekdays[1]))
+    }
+
+    override fun getDate(timeInMillis: Long): CalendarDate {
+        val calendar = Calendar.getInstance(utcTimeZone)
+        calendar.timeInMillis = timeInMillis
+        return CalendarDate(
+            year = calendar[Calendar.YEAR],
+            month = calendar[Calendar.MONTH] + 1,
+            dayOfMonth = calendar[Calendar.DAY_OF_MONTH],
+            utcTimeMillis = timeInMillis
+        )
+    }
+
+    override fun getMonth(timeInMillis: Long): CalendarMonth {
+        val firstDayCalendar = Calendar.getInstance(utcTimeZone)
+        firstDayCalendar.timeInMillis = timeInMillis
+        firstDayCalendar[Calendar.DAY_OF_MONTH] = 1
+        return getMonth(firstDayCalendar)
+    }
+
+    override fun getMonth(date: CalendarDate): CalendarMonth {
+        return getMonth(date.year, date.month)
+    }
+
+    override fun getMonth(year: Int, month: Int): CalendarMonth {
+        val firstDayCalendar = Calendar.getInstance(utcTimeZone)
+        firstDayCalendar.clear()
+        firstDayCalendar[Calendar.YEAR] = year
+        firstDayCalendar[Calendar.MONTH] = month - 1
+        firstDayCalendar[Calendar.DAY_OF_MONTH] = 1
+        return getMonth(firstDayCalendar)
+    }
+
+    override fun getDayOfWeek(date: CalendarDate): Int {
+        return dayInISO8601(date.toCalendar(TimeZone.getDefault())[Calendar.DAY_OF_WEEK])
+    }
+
+    override fun plusMonths(from: CalendarMonth, addedMonthsCount: Int): CalendarMonth {
+        if (addedMonthsCount <= 0) return from
+
+        val laterMonth = from.toCalendar()
+        laterMonth.add(Calendar.MONTH, addedMonthsCount)
+        return getMonth(laterMonth)
+    }
+
+    override fun minusMonths(from: CalendarMonth, subtractedMonthsCount: Int): CalendarMonth {
+        if (subtractedMonthsCount <= 0) return from
+
+        val earlierMonth = from.toCalendar()
+        earlierMonth.add(Calendar.MONTH, -subtractedMonthsCount)
+        return getMonth(earlierMonth)
+    }
+
+    override fun format(month: CalendarMonth, pattern: String): String {
+        val dateFormat = SimpleDateFormat(pattern, Locale.getDefault())
+        dateFormat.timeZone = utcTimeZone
+        dateFormat.isLenient = false
+        return dateFormat.format(month.toCalendar().timeInMillis)
+    }
+
+    override fun format(date: CalendarDate, pattern: String): String {
+        val dateFormat = SimpleDateFormat(pattern, Locale.getDefault())
+        dateFormat.timeZone = utcTimeZone
+        dateFormat.isLenient = false
+        return dateFormat.format(date.toCalendar(utcTimeZone).timeInMillis)
+    }
+
+    override fun parse(date: String, pattern: String): CalendarDate? {
+        val dateFormat = SimpleDateFormat(pattern)
+        dateFormat.timeZone = utcTimeZone
+        dateFormat.isLenient = false
+        return try {
+            val parsedDate = dateFormat.parse(date) ?: return null
+            val calendar = Calendar.getInstance(utcTimeZone)
+            calendar.time = parsedDate
+            CalendarDate(
+                year = calendar[Calendar.YEAR],
+                month = calendar[Calendar.MONTH] + 1,
+                dayOfMonth = calendar[Calendar.DAY_OF_MONTH],
+                utcTimeMillis = calendar.timeInMillis
+            )
+        } catch (pe: ParseException) {
+            null
+        }
+    }
+
+    /**
+     * Returns a given [Calendar] day number as a day representation under ISO-8601, where the first
+     * day is defined as Monday.
+     */
+    private fun dayInISO8601(day: Int): Int {
+        val shiftedDay = (day + 6) % 7
+        return if (shiftedDay == 0) return /* Sunday */ 7 else shiftedDay
+    }
+
+    private fun getMonth(firstDayCalendar: Calendar): CalendarMonth {
+        val difference = dayInISO8601(firstDayCalendar[Calendar.DAY_OF_WEEK]) - firstDayOfWeek
+        val daysFromStartOfWeekToFirstOfMonth = if (difference < 0) {
+            difference + DaysInWeek
+        } else {
+            difference
+        }
+        return CalendarMonth(
+            year = firstDayCalendar[Calendar.YEAR],
+            month = firstDayCalendar[Calendar.MONTH] + 1,
+            numberOfDays = firstDayCalendar.getActualMaximum(Calendar.DAY_OF_MONTH),
+            daysFromStartOfWeekToFirstOfMonth = daysFromStartOfWeekToFirstOfMonth,
+            startUtcTimeMillis = firstDayCalendar.timeInMillis
+        )
+    }
+
+    private fun CalendarMonth.toCalendar(): Calendar {
+        val calendar = Calendar.getInstance(utcTimeZone)
+        calendar.timeInMillis = this.startUtcTimeMillis
+        return calendar
+    }
+
+    private fun CalendarDate.toCalendar(timeZone: TimeZone): Calendar {
+        val calendar = Calendar.getInstance(timeZone)
+        calendar.clear()
+        calendar[Calendar.YEAR] = this.year
+        calendar[Calendar.MONTH] = this.month - 1
+        calendar[Calendar.DAY_OF_MONTH] = this.dayOfMonth
+        return calendar
+    }
+
+    private var utcTimeZone = TimeZone.getTimeZone("UTC")
+}
+
+/**
+ * A [CalendarModel] implementation for API >= 26.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@RequiresApi(Build.VERSION_CODES.O)
+internal class CalendarModelImpl : CalendarModel {
+
+    override val today
+        get(): CalendarDate {
+            val systemLocalDate = LocalDate.now()
+            return CalendarDate(
+                year = systemLocalDate.year,
+                month = systemLocalDate.monthValue,
+                dayOfMonth = systemLocalDate.dayOfMonth,
+                utcTimeMillis = systemLocalDate.atTime(LocalTime.MIDNIGHT)
+                    .atZone(utcTimeZoneId).toInstant().toEpochMilli()
+            )
+        }
+
+    override val firstDayOfWeek: Int = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
+
+    override val weekdayNames: List<Pair<String, String>> =
+        // This will start with Monday as the first day, according to ISO-8601.
+        with(Locale.getDefault()) {
+            DayOfWeek.values().map {
+                it.getDisplayName(
+                    TextStyle.FULL,
+                    /* locale = */ this
+                ) to it.getDisplayName(
+                    TextStyle.NARROW,
+                    /* locale = */ this
+                )
+            }
+        }
+
+    override fun getDate(timeInMillis: Long): CalendarDate {
+        val localDate =
+            Instant.ofEpochMilli(timeInMillis).atZone(utcTimeZoneId).toLocalDate()
+        return CalendarDate(
+            year = localDate.year,
+            month = localDate.monthValue,
+            dayOfMonth = localDate.dayOfMonth,
+            utcTimeMillis = timeInMillis
+        )
+    }
+
+    override fun getMonth(timeInMillis: Long): CalendarMonth {
+        return getMonth(
+            Instant.ofEpochMilli(timeInMillis).atZone(utcTimeZoneId).toLocalDate()
+        )
+    }
+
+    override fun getMonth(date: CalendarDate): CalendarMonth {
+        return getMonth(LocalDate.of(date.year, date.month, 1))
+    }
+
+    override fun getMonth(year: Int, month: Int): CalendarMonth {
+        return getMonth(LocalDate.of(year, month, 1))
+    }
+
+    override fun getDayOfWeek(date: CalendarDate): Int {
+        return date.toLocalDate().dayOfWeek.value
+    }
+
+    override fun plusMonths(from: CalendarMonth, addedMonthsCount: Int): CalendarMonth {
+        if (addedMonthsCount <= 0) return from
+
+        val firstDayLocalDate = from.toLocalDate()
+        val laterMonth = firstDayLocalDate.plusMonths(addedMonthsCount.toLong())
+        return getMonth(laterMonth)
+    }
+
+    override fun minusMonths(from: CalendarMonth, subtractedMonthsCount: Int): CalendarMonth {
+        if (subtractedMonthsCount <= 0) return from
+
+        val firstDayLocalDate = from.toLocalDate()
+        val earlierMonth = firstDayLocalDate.minusMonths(subtractedMonthsCount.toLong())
+        return getMonth(earlierMonth)
+    }
+
+    override fun format(month: CalendarMonth, pattern: String): String {
+        val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(pattern)
+        return month.toLocalDate().format(formatter)
+    }
+
+    override fun format(date: CalendarDate, pattern: String): String {
+        val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(pattern)
+        return date.toLocalDate().format(formatter)
+    }
+
+    override fun parse(date: String, pattern: String): CalendarDate? {
+        // TODO: A DateTimeFormatter can be reused.
+        val formatter = DateTimeFormatter.ofPattern(pattern)
+        return try {
+            val localDate = LocalDate.parse(date, formatter)
+            CalendarDate(
+                year = localDate.year,
+                month = localDate.month.value,
+                dayOfMonth = localDate.dayOfMonth,
+                utcTimeMillis = localDate.atTime(LocalTime.MIDNIGHT)
+                    .atZone(utcTimeZoneId).toInstant().toEpochMilli()
+            )
+        } catch (pe: DateTimeParseException) {
+            null
+        }
+    }
+
+    private fun getMonth(firstDayLocalDate: LocalDate): CalendarMonth {
+        val difference = firstDayLocalDate.dayOfWeek.value - firstDayOfWeek
+        val daysFromStartOfWeekToFirstOfMonth = if (difference < 0) {
+            difference + DaysInWeek
+        } else {
+            difference
+        }
+        val firstDayEpochMillis =
+            firstDayLocalDate.atTime(LocalTime.MIDNIGHT).atZone(utcTimeZoneId).toInstant()
+                .toEpochMilli()
+        return CalendarMonth(
+            year = firstDayLocalDate.year,
+            month = firstDayLocalDate.monthValue,
+            numberOfDays = firstDayLocalDate.lengthOfMonth(),
+            daysFromStartOfWeekToFirstOfMonth = daysFromStartOfWeekToFirstOfMonth,
+            startUtcTimeMillis = firstDayEpochMillis
+        )
+    }
+
+    private fun CalendarMonth.toLocalDate(): LocalDate {
+        return Instant.ofEpochMilli(startUtcTimeMillis).atZone(utcTimeZoneId).toLocalDate()
+    }
+
+    private fun CalendarDate.toLocalDate(): LocalDate {
+        return LocalDate.of(
+            this.year,
+            this.month,
+            this.dayOfMonth
+        )
+    }
+
+    private val utcTimeZoneId = ZoneId.of("UTC")
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/CalendarModel.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/CalendarModel.kt
new file mode 100644
index 0000000..54cbd7b
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/CalendarModel.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2022 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.material3
+
+@ExperimentalMaterial3Api
+internal interface CalendarModel {
+
+    /**
+     * A [CalendarDate] representing the current day.
+     */
+    val today: CalendarDate
+
+    /**
+     * Hold the first day of the week at the current `Locale` as an integer. The integer value
+     * follows the ISO-8601 standard and refer to Monday as 1, and Sunday as 7.
+     */
+    val firstDayOfWeek: Int
+
+    /**
+     * Holds a list of weekday names, starting from Monday as the first day in the list.
+     *
+     * Each item in this list is a [Pair] that holds the full name of the day, and its short
+     * abbreviation letter(s).
+     *
+     * Newer APIs (i.e. API 26+), a [Pair] will hold a full name and the first letter of the
+     * day.
+     * Older APIs that predate API 26 will hold a full name and the first three letters of the day.
+     */
+    val weekdayNames: List<Pair<String, String>>
+
+    /**
+     * Returns a [CalendarDate] from a given _UTC_ time in milliseconds.
+     *
+     * @param timeInMillis UTC milliseconds from the epoch
+     */
+    fun getDate(timeInMillis: Long): CalendarDate
+
+    /**
+     * Returns a [CalendarMonth] from a given _UTC_ time in milliseconds.
+     *
+     * @param timeInMillis UTC milliseconds from the epoch for the first day the month
+     */
+    fun getMonth(timeInMillis: Long): CalendarMonth
+
+    /**
+     * Returns a [CalendarMonth] from a given [CalendarDate].
+     *
+     * Note: This function ignores the [CalendarDate.dayOfMonth] value and just uses the date's
+     * year and month to resolve a [CalendarMonth].
+     *
+     * @param date a [CalendarDate] to resolve into a month
+     */
+    fun getMonth(date: CalendarDate): CalendarMonth
+
+    /**
+     * Returns a [CalendarMonth] from a given [year] and [month].
+     *
+     * @param year the month's year
+     * @param month an integer representing a month (e.g. JANUARY as 1, December as 12)
+     */
+    fun getMonth(year: Int, /* @IntRange(from = 1, to = 12) */ month: Int): CalendarMonth
+
+    /**
+     * Returns a day of week from a given [CalendarDate].
+     *
+     * @param date a [CalendarDate] to resolve
+     */
+    fun getDayOfWeek(date: CalendarDate): Int
+
+    /**
+     * Returns a [CalendarMonth] that is computed by adding a number of months, given as
+     * [addedMonthsCount], to a given month.
+     *
+     * @param from the [CalendarMonth] to add to
+     * @param addedMonthsCount the number of months to add
+     */
+    fun plusMonths(from: CalendarMonth, addedMonthsCount: Int): CalendarMonth
+
+    /**
+     * Returns a [CalendarMonth] that is computed by subtracting a number of months, given as
+     * [subtractedMonthsCount], from a given month.
+     *
+     * @param from the [CalendarMonth] to subtract from
+     * @param subtractedMonthsCount the number of months to subtract
+     */
+    fun minusMonths(from: CalendarMonth, subtractedMonthsCount: Int): CalendarMonth
+
+    /**
+     * Formats a [CalendarMonth] into a string with a given date format pattern.
+     *
+     * @param month a [CalendarMonth] to format
+     * @param pattern a date format pattern
+     */
+    fun format(month: CalendarMonth, pattern: String): String
+
+    /**
+     * Formats a [CalendarDate] into a string with a given date format pattern.
+     *
+     * @param date a [CalendarDate] to format
+     * @param pattern a date format pattern
+     */
+    fun format(date: CalendarDate, pattern: String): String
+
+    /**
+     * Parses a date string into a [CalendarDate].
+     *
+     * @param date a date string
+     * @param pattern the expected date pattern to be used for parsing the date string
+     * @return a [CalendarDate], or a `null` in case the parsing failed
+     */
+    fun parse(date: String, pattern: String): CalendarDate?
+}
+
+/**
+ * Represents a calendar date.
+ *
+ * @param year the date's year
+ * @param month the date's month
+ * @param dayOfMonth the date's day of month
+ * @param utcTimeMillis the date representation in _UTC_ milliseconds from the epoch
+ */
+@ExperimentalMaterial3Api
+internal data class CalendarDate(
+    val year: Int,
+    val month: Int,
+    val dayOfMonth: Int,
+    val utcTimeMillis: Long
+) : Comparable<CalendarDate> {
+    override operator fun compareTo(other: CalendarDate): Int =
+        this.utcTimeMillis.compareTo(other.utcTimeMillis)
+}
+
+/**
+ * Represents a calendar month.
+ *
+ * @param year the month's year
+ * @param month the calendar month as an integer (e.g. JANUARY as 1, December as 12)
+ * @param numberOfDays the number of days in the month
+ * @param daysFromStartOfWeekToFirstOfMonth the number of days from the start of the week to the
+ * first day of the month
+ * @param startUtcTimeMillis the first day of the month in _UTC_ milliseconds from the epoch
+ */
+@ExperimentalMaterial3Api
+internal data class CalendarMonth(
+    val year: Int,
+    val month: Int,
+    val numberOfDays: Int,
+    val daysFromStartOfWeekToFirstOfMonth: Int,
+    val startUtcTimeMillis: Long
+) {
+
+    /**
+     * The last _UTC_ milliseconds from the epoch of the month (i.e. the last millisecond of the
+     * last day of the month)
+     */
+    val endUtcTimeMillis: Long = startUtcTimeMillis + (numberOfDays * MillisecondsIn24Hours) - 1
+
+    /**
+     * Returns the position of a [CalendarMonth] within given years range.
+     */
+    internal fun indexIn(years: IntRange): Int {
+        return (year - years.first) * 12 + month - 1
+    }
+}
+
+internal const val DaysInWeek: Int = 7
+internal const val MillisecondsIn24Hours = 86400000L
diff --git a/compose/ui/ui-graphics/benchmark/src/androidTest/java/androidx/compose/ui/graphics/benchmark/VectorBenchmarkWithTracing.kt b/compose/ui/ui-graphics/benchmark/src/androidTest/java/androidx/compose/ui/graphics/benchmark/VectorBenchmarkWithTracing.kt
deleted file mode 100644
index 67f4487..0000000
--- a/compose/ui/ui-graphics/benchmark/src/androidTest/java/androidx/compose/ui/graphics/benchmark/VectorBenchmarkWithTracing.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.graphics.benchmark
-
-import androidx.benchmark.junit4.PerfettoTraceRule
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import org.junit.Rule
-import org.junit.runner.RunWith
-
-/**
- * Duplicate of [VectorBenchmark], but which adds tracing.
- */
-@Suppress("ClassName")
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class VectorBenchmarkWithTracing : VectorBenchmark() {
-    @OptIn(ExperimentalPerfettoCaptureApi::class)
-    @get:Rule
-    val perfettoTraceRule = PerfettoTraceRule()
-}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidPargraphExt.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphExt.kt
similarity index 83%
rename from compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidPargraphExt.kt
rename to compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphExt.kt
index 0a1cbd0..a76fc2b 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidPargraphExt.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphExt.kt
@@ -18,17 +18,14 @@
 
 import android.graphics.Bitmap
 import android.graphics.Canvas
-import androidx.compose.ui.text.style.TextDecoration
 import kotlin.math.ceil
 
-internal fun AndroidParagraph.bitmap(
-    textDecoration: TextDecoration? = null
-): Bitmap {
+internal fun AndroidParagraph.bitmap(): Bitmap {
     val bitmap = Bitmap.createBitmap(
         ceil(this.width).toInt(),
         ceil(this.height).toInt(),
         Bitmap.Config.ARGB_8888
     )
-    this.paint(androidx.compose.ui.graphics.Canvas(Canvas(bitmap)), textDecoration = textDecoration)
+    this.paint(androidx.compose.ui.graphics.Canvas(Canvas(bitmap)))
     return bitmap
 }
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
index 946e6c0..f4b52f8 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
@@ -21,6 +21,7 @@
 import android.text.TextPaint
 import android.text.style.AbsoluteSizeSpan
 import android.text.style.BackgroundColorSpan
+import android.text.style.CharacterStyle
 import android.text.style.ForegroundColorSpan
 import android.text.style.LeadingMarginSpan
 import android.text.style.LocaleSpan
@@ -1436,6 +1437,17 @@
     }
 
     @Test
+    fun testSpanStyle_textDecoration_underline_appliedOnTextPaint() {
+        val paragraph = simpleParagraph(
+            text = "",
+            style = TextStyle(textDecoration = TextDecoration.Underline),
+            width = 0.0f
+        )
+
+        assertThat(paragraph.textPaint.isUnderlineText).isTrue()
+    }
+
+    @Test
     fun testSpanStyle_textDecoration_underline_appliedAsSpan() {
         val text = "abc"
         val paragraph = simpleParagraph(
@@ -1444,21 +1456,18 @@
             width = 0.0f
         )
 
-        assertThat(paragraph.charSequence)
-            .hasSpan(TextDecorationSpan::class, 0, text.length) { it.isUnderlineText }
+        assertThat(paragraph.charSequence).hasSpan(CharacterStyle::class, 0, text.length)
     }
 
     @Test
-    fun testSpanStyle_textDecoration_lineThrough_appliedAsSpan() {
-        val text = "abc"
+    fun testSpanStyle_textDecoration_lineThrough_appliedOnTextPaint() {
         val paragraph = simpleParagraph(
-            text = text,
+            text = "",
             style = TextStyle(textDecoration = TextDecoration.LineThrough),
             width = 0.0f
         )
 
-        assertThat(paragraph.charSequence)
-            .hasSpan(TextDecorationSpan::class, 0, text.length) { it.isStrikethroughText }
+        assertThat(paragraph.textPaint.isStrikeThruText).isTrue()
     }
 
     @OptIn(ExperimentalTextApi::class)
@@ -1519,6 +1528,7 @@
             style = TextStyle(textDecoration = null),
             width = 0.0f
         )
+        assertThat(paragraph.textPaint.isUnderlineText).isFalse()
 
         val canvas = Canvas(android.graphics.Canvas())
         paragraph.paint(canvas, textDecoration = TextDecoration.Underline)
@@ -1534,12 +1544,9 @@
             ),
             width = 0.0f
         )
-        // Underline text is not applied on TextPaint initially. It is set as a span.
-        // Once drawn, this span gets applied on the TextPaint.
-        val canvas = Canvas(android.graphics.Canvas())
-        paragraph.paint(canvas, textDecoration = TextDecoration.Underline)
         assertThat(paragraph.textPaint.isUnderlineText).isTrue()
 
+        val canvas = Canvas(android.graphics.Canvas())
         paragraph.paint(canvas, textDecoration = TextDecoration.None)
         assertThat(paragraph.textPaint.isUnderlineText).isFalse()
     }
@@ -1553,12 +1560,9 @@
             ),
             width = 0.0f
         )
-        // Underline text is not applied on TextPaint initially. It is set as a span.
-        // Once drawn, this span gets applied on the TextPaint.
-        val canvas = Canvas(android.graphics.Canvas())
-        paragraph.paint(canvas, textDecoration = TextDecoration.Underline)
         assertThat(paragraph.textPaint.isUnderlineText).isTrue()
 
+        val canvas = Canvas(android.graphics.Canvas())
         paragraph.paint(canvas, textDecoration = null)
         assertThat(paragraph.textPaint.isUnderlineText).isTrue()
     }
@@ -1953,6 +1957,32 @@
         )
     }
 
+    @OptIn(ExperimentalTextApi::class)
+    @Test
+    fun shaderBrushSpan_createsShaderOnlyOnce() {
+        val fontSize = 20
+        val text = "abcdef"
+        val shaderBrush = object : ShaderBrush() {
+            var callCount = 0
+            private val brush = Brush.linearGradient(listOf(Color.Red, Color.Blue)) as ShaderBrush
+            override fun createShader(size: Size): android.graphics.Shader {
+                callCount++
+                return brush.createShader(size)
+            }
+        }
+        val spanStyle = SpanStyle(brush = shaderBrush)
+        val paragraph = simpleParagraph(
+            text = "abcdef",
+            width = 2f * fontSize * text.length,
+            style = TextStyle(fontSize = fontSize.sp),
+            spanStyles = listOf(AnnotatedString.Range(spanStyle, 0, 2))
+        )
+
+        // Call paint on paragraph multiple times
+        repeat(5) { paragraph.bitmap() }
+        assertThat(shaderBrush.callCount).isEqualTo(1)
+    }
+
     @Test
     fun drawText_withUnderlineStyle_equalToUnderlinePaint() = with(defaultDensity) {
         val fontSize = 30.sp
@@ -1985,32 +2015,6 @@
         assertThat(bitmapWithSpan).isEqualToBitmap(bitmapNoSpan)
     }
 
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun shaderBrushSpan_createsShaderOnlyOnce() {
-        val fontSize = 20
-        val text = "abcdef"
-        val shaderBrush = object : ShaderBrush() {
-            var callCount = 0
-            private val brush = Brush.linearGradient(listOf(Color.Red, Color.Blue)) as ShaderBrush
-            override fun createShader(size: Size): android.graphics.Shader {
-                callCount++
-                return brush.createShader(size)
-            }
-        }
-        val spanStyle = SpanStyle(brush = shaderBrush)
-        val paragraph = simpleParagraph(
-            text = "abcdef",
-            width = 2f * fontSize * text.length,
-            style = TextStyle(fontSize = fontSize.sp),
-            spanStyles = listOf(AnnotatedString.Range(spanStyle, 0, 2))
-        )
-
-        // Call paint on paragraph multiple times
-        repeat(5) { paragraph.bitmap() }
-        assertThat(shaderBrush.callCount).isEqualTo(1)
-    }
-
     private fun simpleParagraph(
         text: String = "",
         spanStyles: List<AnnotatedString.Range<SpanStyle>> = listOf(),
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
index c2d97d7..7088ec4 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import androidx.test.filters.Suppress
 import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -1178,7 +1179,11 @@
         )
 
         val bitmapWithSpan = multiParagraph.bitmap()
-        val bitmapNoSpan = multiParagraph2.bitmap()
+        // Our text rendering stack relies on the fact that given textstyle is also passed to draw
+        // functions of TextLayoutResult, MultiParagraph, Paragraph. If Underline is not specified
+        // here, it would be removed while drawing the MultiParagraph. We are simply mimicking
+        // what TextPainter does.
+        val bitmapNoSpan = multiParagraph2.bitmap(textDecoration = TextDecoration.Underline)
 
         assertThat(bitmapWithSpan).isEqualToBitmap(bitmapNoSpan)
     }
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt
index 0f04167..e4e8f33 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt
@@ -259,7 +259,7 @@
     }
 
     @Test
-    fun textDecorationUnderline_shouldBeLeftAsSpan() {
+    fun textDecorationUnderline_shouldBeAppliedToPaint() {
         val textDecoration = TextDecoration.Underline
         val spanStyle = SpanStyle(textDecoration = textDecoration)
         val tp = AndroidTextPaint(0, density.density)
@@ -267,12 +267,12 @@
 
         val notApplied = tp.applySpanStyle(spanStyle, resolveTypeface, density)
 
-        assertThat(tp.isUnderlineText).isEqualTo(false)
-        assertThat(notApplied.textDecoration).isEqualTo(textDecoration)
+        assertThat(tp.isUnderlineText).isEqualTo(true)
+        assertThat(notApplied.textDecoration).isEqualTo(null)
     }
 
     @Test
-    fun textDecorationLineThrough_shouldBeLeftAsSpan() {
+    fun textDecorationLineThrough_shouldBeAppliedToPaint() {
         val textDecoration = TextDecoration.LineThrough
         val spanStyle = SpanStyle(textDecoration = textDecoration)
         val tp = AndroidTextPaint(0, density.density)
@@ -280,13 +280,14 @@
 
         val notApplied = tp.applySpanStyle(spanStyle, resolveTypeface, density)
 
-        assertThat(tp.isStrikeThruText).isEqualTo(false)
-        assertThat(notApplied.textDecoration).isEqualTo(textDecoration)
+        assertThat(tp.isStrikeThruText).isEqualTo(true)
+        assertThat(notApplied.textDecoration).isEqualTo(null)
     }
 
     @Test
-    fun textDecorationNone_shouldNotBeLeftAsSpan() {
-        val textDecoration = TextDecoration.None
+    fun textDecorationCombined_shouldBeAppliedToPaint() {
+        val textDecoration =
+            TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline))
         val spanStyle = SpanStyle(textDecoration = textDecoration)
         val tp = AndroidTextPaint(0, density.density)
         tp.isUnderlineText = false
@@ -294,9 +295,9 @@
 
         val notApplied = tp.applySpanStyle(spanStyle, resolveTypeface, density)
 
-        assertThat(tp.isUnderlineText).isEqualTo(false)
-        assertThat(tp.isStrikeThruText).isEqualTo(false)
-        assertThat(notApplied.textDecoration).isNull()
+        assertThat(tp.isUnderlineText).isEqualTo(true)
+        assertThat(tp.isStrikeThruText).isEqualTo(true)
+        assertThat(notApplied.textDecoration).isEqualTo(null)
     }
 
     @Test
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
index 60832dd..9c943d0 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
@@ -18,6 +18,8 @@
 
 import android.graphics.Typeface
 import android.text.SpannableString
+import android.text.TextPaint
+import android.text.style.CharacterStyle
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.DefaultIncludeFontPadding
 import androidx.compose.ui.text.ExperimentalTextApi
@@ -31,9 +33,11 @@
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.platform.extensions.setLineHeight
 import androidx.compose.ui.text.platform.extensions.setPlaceholders
+import androidx.compose.ui.text.platform.extensions.setSpan
 import androidx.compose.ui.text.platform.extensions.setSpanStyles
 import androidx.compose.ui.text.platform.extensions.setTextIndent
 import androidx.compose.ui.text.style.LineHeightStyle
+import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextIndent
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.isUnspecified
@@ -58,6 +62,15 @@
 
     val spannableString = SpannableString(text)
 
+    // b/199939617
+    // Due to a bug in the platform's native drawText stack, some CJK characters cause a bolder
+    // than intended underline to be painted when TextDecoration is set to Underline.
+    // If there's a CharacterStyle span that takes the entire length of the text, even if
+    // it's no-op, it causes a different native call to render the text that prevents the bug.
+    if (contextTextStyle.textDecoration == TextDecoration.Underline) {
+        spannableString.setSpan(NoopSpan, 0, text.length)
+    }
+
     if (contextTextStyle.isIncludeFontPaddingEnabled() &&
         contextTextStyle.lineHeightStyle == null
     ) {
@@ -95,4 +108,8 @@
 @Suppress("DEPRECATION")
 internal fun TextStyle.isIncludeFontPaddingEnabled(): Boolean {
     return platformStyle?.paragraphStyle?.includeFontPadding ?: DefaultIncludeFontPadding
+}
+
+private val NoopSpan = object : CharacterStyle() {
+    override fun updateDrawState(p0: TextPaint?) {}
 }
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
index 4a57356..2c9eb85 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
@@ -30,7 +30,6 @@
 import androidx.compose.ui.text.intl.LocaleList
 import androidx.compose.ui.text.platform.AndroidTextPaint
 import androidx.compose.ui.text.style.BaselineShift
-import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextGeometricTransform
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.TextUnit
@@ -106,7 +105,7 @@
     // Paragraph.paint will receive a proper Size after layout is completed.
     setBrush(style.brush, Size.Unspecified, style.alpha)
     setShadow(style.shadow)
-    // Skip textDecoration (b/199939617). TextDecoration should be applied as a span.
+    setTextDecoration(style.textDecoration)
     setDrawStyle(style.drawStyle)
 
     // letterSpacing with unit Sp needs to be handled by span.
@@ -129,9 +128,6 @@
             null
         } else {
             style.baselineShift
-        },
-        textDecoration = style.textDecoration.takeIf {
-            style.textDecoration != TextDecoration.None
         }
     )
 }
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt
index a0f47fc..82f7d09 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt
@@ -57,24 +57,27 @@
             canvas.save()
             canvas.clipRect(bounds)
         }
+        val resolvedSpanStyle = resolveSpanStyleDefaults(
+            textLayoutResult.layoutInput.style.spanStyle
+        )
         try {
-            val brush = textLayoutResult.layoutInput.style.brush
+            val brush = resolvedSpanStyle.brush
             if (brush != null) {
                 textLayoutResult.multiParagraph.paint(
-                    canvas,
-                    brush,
-                    textLayoutResult.layoutInput.style.alpha,
-                    textLayoutResult.layoutInput.style.shadow,
-                    textLayoutResult.layoutInput.style.textDecoration,
-                    textLayoutResult.layoutInput.style.drawStyle
+                    canvas = canvas,
+                    brush = brush,
+                    alpha = resolvedSpanStyle.alpha,
+                    shadow = resolvedSpanStyle.shadow,
+                    decoration = resolvedSpanStyle.textDecoration,
+                    drawStyle = resolvedSpanStyle.drawStyle
                 )
             } else {
                 textLayoutResult.multiParagraph.paint(
-                    canvas,
-                    textLayoutResult.layoutInput.style.color,
-                    textLayoutResult.layoutInput.style.shadow,
-                    textLayoutResult.layoutInput.style.textDecoration,
-                    textLayoutResult.layoutInput.style.drawStyle
+                    canvas = canvas,
+                    color = resolvedSpanStyle.color,
+                    shadow = resolvedSpanStyle.shadow,
+                    decoration = resolvedSpanStyle.textDecoration,
+                    drawStyle = resolvedSpanStyle.drawStyle
                 )
             }
         } finally {
@@ -323,7 +326,8 @@
 
 private fun DrawTransform.clip(textLayoutResult: TextLayoutResult) {
     if (textLayoutResult.hasVisualOverflow &&
-        textLayoutResult.layoutInput.overflow != TextOverflow.Visible) {
+        textLayoutResult.layoutInput.overflow != TextOverflow.Visible
+    ) {
         clipRect(
             left = 0f,
             top = 0f,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index 46de247..28504c3 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.platform
 
+import android.accessibilityservice.AccessibilityServiceInfo
 import android.content.Context
 import android.graphics.RectF
 import android.graphics.Region
@@ -82,6 +83,7 @@
 import androidx.compose.ui.text.platform.toAccessibilitySpannableString
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachIndexed
 import androidx.core.view.AccessibilityDelegateCompat
@@ -188,10 +190,44 @@
     private val accessibilityManager: AccessibilityManager =
         view.context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
     internal var accessibilityForceEnabledForTesting = false
-    private val isAccessibilityEnabled
+
+    /**
+     * True if any accessibility service enabled in the system, except the UIAutomator (as it
+     * doesn't appear in the list of enabled services)
+     */
+    private val isEnabled: Boolean
+        get() {
+            // checking the list allows us to filter out the UIAutomator which doesn't appear in it
+            val enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
+                AccessibilityServiceInfo.FEEDBACK_ALL_MASK
+            )
+            return accessibilityForceEnabledForTesting ||
+                accessibilityManager.isEnabled && enabledServices.isNotEmpty()
+        }
+
+    /**
+     * True if accessibility service with the touch exploration (e.g. Talkback) is enabled in the
+     * system.
+     * Note that UIAutomator doesn't request touch exploration therefore returns false
+     */
+    private val isTouchExplorationEnabled
         get() = accessibilityForceEnabledForTesting ||
-            accessibilityManager.isEnabled &&
-            accessibilityManager.isTouchExplorationEnabled
+                accessibilityManager.isEnabled && accessibilityManager.isTouchExplorationEnabled
+
+    /** True if an accessibility service that listens for the event of type [eventType] is enabled
+     * in the system.
+     * Note that UIAutomator will always return false as it doesn't appear in the list of enabled
+     * services
+     */
+    private fun isEnabledForEvent(eventType: Int): Boolean {
+        val enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
+            AccessibilityServiceInfo.FEEDBACK_ALL_MASK
+        )
+        return enabledServices.fastFirstOrNull {
+            it.eventTypes.and(eventType) != 0
+        } != null || accessibilityForceEnabledForTesting
+    }
+
     private val handler = Handler(Looper.getMainLooper())
     private var nodeProvider: AccessibilityNodeProviderCompat =
         AccessibilityNodeProviderCompat(MyNodeProvider())
@@ -978,7 +1014,7 @@
      * @return Whether this virtual view actually took accessibility focus.
      */
     private fun requestAccessibilityFocus(virtualViewId: Int): Boolean {
-        if (!isAccessibilityEnabled) {
+        if (!isTouchExplorationEnabled) {
             return false
         }
         // TODO: Check virtual view visibility.
@@ -1029,7 +1065,7 @@
         contentChangeType: Int? = null,
         contentDescription: List<String>? = null
     ): Boolean {
-        if (virtualViewId == InvalidId || !isAccessibilityEnabled) {
+        if (virtualViewId == InvalidId || !isEnabledForEvent(eventType)) {
             return false
         }
 
@@ -1051,7 +1087,8 @@
      * @return true if the event was sent successfully.
      */
     private fun sendEvent(event: AccessibilityEvent): Boolean {
-        if (!isAccessibilityEnabled) {
+        // only send an event if there's an enabled service listening for events of this type
+        if (!isEnabledForEvent(event.eventType)) {
             return false
         }
 
@@ -1499,7 +1536,7 @@
      * @return Whether the hover event was handled.
      */
     fun dispatchHoverEvent(event: MotionEvent): Boolean {
-        if (!isAccessibilityEnabled) {
+        if (!isTouchExplorationEnabled) {
             return false
         }
 
@@ -1637,7 +1674,7 @@
         // later, we can refresh currentSemanticsNodes if currentSemanticsNodes is stale.
         currentSemanticsNodesInvalidated = true
 
-        if (isAccessibilityEnabled && !checkingForSemanticsChanges) {
+        if (isEnabled && !checkingForSemanticsChanges) {
             checkingForSemanticsChanges = true
             handler.post(semanticsChangeChecker)
         }
@@ -1652,7 +1689,7 @@
         try {
             val subtreeChangedSemanticsNodesIds = ArraySet<Int>()
             for (notification in boundsUpdateChannel) {
-                if (isAccessibilityEnabled) {
+                if (isEnabled) {
                     for (i in subtreeChangedLayoutNodes.indices) {
                         @Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
                         sendSubtreeChangeAccessibilityEvents(
@@ -1693,7 +1730,7 @@
         // currentSemanticsNodesInvalidated up to date so that when accessibility is turned on
         // later, we can refresh currentSemanticsNodes if currentSemanticsNodes is stale.
         currentSemanticsNodesInvalidated = true
-        if (!isAccessibilityEnabled) {
+        if (!isEnabled) {
             return
         }
         // The layout change of a LayoutNode will also affect its children, so even if it doesn't
diff --git a/constraintlayout/constraintlayout-compose/api/current.txt b/constraintlayout/constraintlayout-compose/api/current.txt
index 888c103..0c83242 100644
--- a/constraintlayout/constraintlayout-compose/api/current.txt
+++ b/constraintlayout/constraintlayout-compose/api/current.txt
@@ -36,6 +36,7 @@
     method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
     method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
     method public androidx.constraintlayout.compose.Dimension getHeight();
+    method public float getHorizontalBias();
     method public float getHorizontalChainWeight();
     method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
     method public float getPivotX();
@@ -50,6 +51,7 @@
     method public float getTranslationX();
     method public float getTranslationY();
     method public float getTranslationZ();
+    method public float getVerticalBias();
     method public float getVerticalChainWeight();
     method public androidx.constraintlayout.compose.Visibility getVisibility();
     method public androidx.constraintlayout.compose.Dimension getWidth();
@@ -60,6 +62,7 @@
     method public void resetTransforms();
     method public void setAlpha(float);
     method public void setHeight(androidx.constraintlayout.compose.Dimension);
+    method public void setHorizontalBias(float);
     method public void setHorizontalChainWeight(float);
     method public void setPivotX(float);
     method public void setPivotY(float);
@@ -71,6 +74,7 @@
     method public void setTranslationX(float);
     method public void setTranslationY(float);
     method public void setTranslationZ(float);
+    method public void setVerticalBias(float);
     method public void setVerticalChainWeight(float);
     method public void setVisibility(androidx.constraintlayout.compose.Visibility);
     method public void setWidth(androidx.constraintlayout.compose.Dimension);
@@ -81,6 +85,7 @@
     property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
     property public final androidx.constraintlayout.compose.VerticalAnchorable end;
     property public final androidx.constraintlayout.compose.Dimension height;
+    property public final float horizontalBias;
     property public final float horizontalChainWeight;
     property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
     property public final float pivotX;
@@ -95,6 +100,7 @@
     property public final float translationX;
     property public final float translationY;
     property public final float translationZ;
+    property public final float verticalBias;
     property public final float verticalChainWeight;
     property public final androidx.constraintlayout.compose.Visibility visibility;
     property public final androidx.constraintlayout.compose.Dimension width;
@@ -151,6 +157,9 @@
     method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
     method protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
     method public void reset();
+    method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
+    method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
+    method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
     property protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
   }
 
diff --git a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
index 9bd9dc8..fd40d95 100644
--- a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
@@ -67,6 +67,7 @@
     method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
     method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
     method public androidx.constraintlayout.compose.Dimension getHeight();
+    method public float getHorizontalBias();
     method public float getHorizontalChainWeight();
     method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
     method public float getPivotX();
@@ -81,6 +82,7 @@
     method public float getTranslationX();
     method public float getTranslationY();
     method public float getTranslationZ();
+    method public float getVerticalBias();
     method public float getVerticalChainWeight();
     method public androidx.constraintlayout.compose.Visibility getVisibility();
     method public androidx.constraintlayout.compose.Dimension getWidth();
@@ -91,6 +93,7 @@
     method public void resetTransforms();
     method public void setAlpha(float);
     method public void setHeight(androidx.constraintlayout.compose.Dimension);
+    method public void setHorizontalBias(float);
     method public void setHorizontalChainWeight(float);
     method public void setPivotX(float);
     method public void setPivotY(float);
@@ -102,6 +105,7 @@
     method public void setTranslationX(float);
     method public void setTranslationY(float);
     method public void setTranslationZ(float);
+    method public void setVerticalBias(float);
     method public void setVerticalChainWeight(float);
     method public void setVisibility(androidx.constraintlayout.compose.Visibility);
     method public void setWidth(androidx.constraintlayout.compose.Dimension);
@@ -112,6 +116,7 @@
     property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
     property public final androidx.constraintlayout.compose.VerticalAnchorable end;
     property public final androidx.constraintlayout.compose.Dimension height;
+    property public final float horizontalBias;
     property public final float horizontalChainWeight;
     property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
     property public final float pivotX;
@@ -126,6 +131,7 @@
     property public final float translationX;
     property public final float translationY;
     property public final float translationZ;
+    property public final float verticalBias;
     property public final float verticalChainWeight;
     property public final androidx.constraintlayout.compose.Visibility visibility;
     property public final androidx.constraintlayout.compose.Dimension width;
@@ -182,6 +188,9 @@
     method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
     method protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
     method public void reset();
+    method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
+    method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
+    method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
     property protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
   }
 
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_current.txt b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
index b2a7877..d11ace4 100644
--- a/constraintlayout/constraintlayout-compose/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
@@ -36,6 +36,7 @@
     method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
     method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
     method public androidx.constraintlayout.compose.Dimension getHeight();
+    method public float getHorizontalBias();
     method public float getHorizontalChainWeight();
     method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
     method public float getPivotX();
@@ -50,6 +51,7 @@
     method public float getTranslationX();
     method public float getTranslationY();
     method public float getTranslationZ();
+    method public float getVerticalBias();
     method public float getVerticalChainWeight();
     method public androidx.constraintlayout.compose.Visibility getVisibility();
     method public androidx.constraintlayout.compose.Dimension getWidth();
@@ -60,6 +62,7 @@
     method public void resetTransforms();
     method public void setAlpha(float);
     method public void setHeight(androidx.constraintlayout.compose.Dimension);
+    method public void setHorizontalBias(float);
     method public void setHorizontalChainWeight(float);
     method public void setPivotX(float);
     method public void setPivotY(float);
@@ -71,6 +74,7 @@
     method public void setTranslationX(float);
     method public void setTranslationY(float);
     method public void setTranslationZ(float);
+    method public void setVerticalBias(float);
     method public void setVerticalChainWeight(float);
     method public void setVisibility(androidx.constraintlayout.compose.Visibility);
     method public void setWidth(androidx.constraintlayout.compose.Dimension);
@@ -81,6 +85,7 @@
     property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
     property public final androidx.constraintlayout.compose.VerticalAnchorable end;
     property public final androidx.constraintlayout.compose.Dimension height;
+    property public final float horizontalBias;
     property public final float horizontalChainWeight;
     property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
     property public final float pivotX;
@@ -95,6 +100,7 @@
     property public final float translationX;
     property public final float translationY;
     property public final float translationZ;
+    property public final float verticalBias;
     property public final float verticalChainWeight;
     property public final androidx.constraintlayout.compose.Visibility visibility;
     property public final androidx.constraintlayout.compose.Dimension width;
@@ -151,6 +157,9 @@
     method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
     method protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
     method public void reset();
+    method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
+    method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
+    method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
     property protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
     field @kotlin.PublishedApi internal int helpersHashCode;
   }
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt
index bbfe49b..5fb7268 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt
@@ -21,7 +21,9 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.dp
@@ -114,7 +116,11 @@
         val boxSizes = arrayOf(10.dp, 20.dp, 30.dp)
         val margin = 10.dp
         rule.setContent {
-            ConstraintLayout(Modifier.size(rootSize)) {
+            ConstraintLayout(
+                Modifier
+                    .background(Color.LightGray)
+                    .size(rootSize)
+            ) {
                 val (box0, box1, box2) = createRefs()
                 val chain0 = createHorizontalChain(box0, box1, chainStyle = ChainStyle.Packed)
                 constrain(chain0) {
@@ -170,4 +176,288 @@
         rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(box1Left, box1Top)
         rule.onNodeWithTag("box2").assertPositionInRootIsEqualTo(margin, margin)
     }
+
+    @Test
+    fun testHorizontalPacked_withMargins() {
+        val rootSize = 100.dp
+        val boxSizes = arrayOf(10.dp, 20.dp, 30.dp)
+        val boxMargin = 5.dp
+        val boxGoneMargin = 7.dp
+        val constraintSet = ConstraintSet {
+            val box0 = createRefFor("box0")
+            val boxGone = createRefFor("boxGone")
+            val box1 = createRefFor("box1")
+            val box2 = createRefFor("box2")
+
+            createHorizontalChain(
+                box0.withChainParams(
+                    startMargin = 0.dp,
+                    endMargin = boxMargin, // Not applied since the next box is Gone
+                    endGoneMargin = boxGoneMargin
+                ),
+                boxGone.withChainParams(
+                    // None of these margins should apply since it's Gone
+                    startMargin = 100.dp,
+                    endMargin = 100.dp,
+                    startGoneMargin = 100.dp,
+                    endGoneMargin = 100.dp
+                ),
+                box1,
+                box2.withHorizontalChainParams(startMargin = boxMargin, endMargin = 0.dp),
+                chainStyle = ChainStyle.Packed
+            )
+
+            constrain(box0) {
+                width = Dimension.value(boxSizes[0])
+                height = Dimension.value(boxSizes[0])
+                centerVerticallyTo(parent)
+            }
+            constrain(boxGone) {
+                width = Dimension.value(boxSizes[1])
+                height = Dimension.value(boxSizes[1])
+                centerVerticallyTo(box0)
+
+                visibility = Visibility.Gone
+            }
+            constrain(box1) {
+                width = Dimension.value(boxSizes[1])
+                height = Dimension.value(boxSizes[1])
+                centerVerticallyTo(box0)
+            }
+            constrain(box2) {
+                width = Dimension.value(boxSizes[2])
+                height = Dimension.value(boxSizes[2])
+                centerVerticallyTo(box0)
+            }
+        }
+        rule.setContent {
+            ConstraintLayout(
+                modifier = Modifier
+                    .background(Color.LightGray)
+                    .size(rootSize),
+                constraintSet = constraintSet
+            ) {
+                Box(
+                    modifier = Modifier
+                        .background(Color.Red)
+                        .layoutTestId("box0")
+                )
+                Box(
+                    modifier = Modifier
+                        .background(Color.Blue)
+                        .layoutTestId("box1")
+                )
+                Box(
+                    modifier = Modifier
+                        .background(Color.Green)
+                        .layoutTestId("box2")
+                )
+            }
+        }
+        rule.waitForIdle()
+
+        val totalMargins = boxMargin + boxGoneMargin
+        val totalChainSpace = boxSizes[0] + boxSizes[1] + boxSizes[2] + totalMargins
+
+        val box0Left = (rootSize - totalChainSpace) * 0.5f
+        val box0Top = (rootSize - boxSizes[0]) * 0.5f
+
+        val box1Left = box0Left + boxSizes[0] + boxGoneMargin
+        val box1Top = (rootSize - boxSizes[1]) * 0.5f
+
+        val box2Left = box1Left + boxSizes[1] + boxMargin
+        val box2Top = (rootSize - boxSizes[2]) * 0.5f
+
+        rule.onNodeWithTag("box0").assertPositionInRootIsEqualTo(box0Left, box0Top)
+        rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(box1Left, box1Top)
+        rule.onNodeWithTag("box2").assertPositionInRootIsEqualTo(box2Left, box2Top)
+    }
+
+    @Test
+    fun testVerticalPacked_withMargins() {
+        val rootSize = 100.dp
+        val boxSizes = arrayOf(10.dp, 20.dp, 30.dp)
+        val boxMargin = 5.dp
+        val boxGoneMargin = 7.dp
+        val constraintSet = ConstraintSet {
+            val box0 = createRefFor("box0")
+            val boxGone = createRefFor("boxGone")
+            val box1 = createRefFor("box1")
+            val box2 = createRefFor("box2")
+
+            createVerticalChain(
+                box0.withChainParams(
+                    topMargin = 0.dp,
+                    bottomMargin = boxMargin, // Not applied since the next box is Gone
+                    bottomGoneMargin = boxGoneMargin
+                ),
+                boxGone.withChainParams(
+                    // None of these margins should apply since it's Gone
+                    topMargin = 100.dp,
+                    bottomMargin = 100.dp,
+                    topGoneMargin = 100.dp,
+                    bottomGoneMargin = 100.dp
+                ),
+                box1,
+                box2.withVerticalChainParams(topMargin = boxMargin, bottomMargin = 0.dp),
+                chainStyle = ChainStyle.Packed
+            )
+
+            constrain(box0) {
+                width = Dimension.value(boxSizes[0])
+                height = Dimension.value(boxSizes[0])
+                centerHorizontallyTo(parent)
+            }
+            constrain(boxGone) {
+                width = Dimension.value(100.dp) // Dimensions won't matter since it's Gone
+                height = Dimension.value(100.dp) // Dimensions won't matter since it's Gone
+                centerHorizontallyTo(box0)
+
+                visibility = Visibility.Gone
+            }
+            constrain(box1) {
+                width = Dimension.value(boxSizes[1])
+                height = Dimension.value(boxSizes[1])
+                centerHorizontallyTo(box0)
+            }
+            constrain(box2) {
+                width = Dimension.value(boxSizes[2])
+                height = Dimension.value(boxSizes[2])
+                centerHorizontallyTo(box0)
+            }
+        }
+        rule.setContent {
+            ConstraintLayout(
+                modifier = Modifier
+                    .background(Color.LightGray)
+                    .size(rootSize),
+                constraintSet = constraintSet
+            ) {
+                Box(
+                    modifier = Modifier
+                        .background(Color.Red)
+                        .layoutTestId("box0")
+                )
+                Box(
+                    modifier = Modifier
+                        .background(Color.Blue)
+                        .layoutTestId("box1")
+                )
+                Box(
+                    modifier = Modifier
+                        .background(Color.Green)
+                        .layoutTestId("box2")
+                )
+            }
+        }
+        rule.waitForIdle()
+
+        val totalMargins = boxMargin + boxGoneMargin
+        val totalChainSpace = boxSizes[0] + boxSizes[1] + boxSizes[2] + totalMargins
+
+        val box0Left = (rootSize - boxSizes[0]) * 0.5f
+        val box0Top = (rootSize - totalChainSpace) * 0.5f
+
+        val box1Left = (rootSize - boxSizes[1]) * 0.5f
+        val box1Top = box0Top + boxSizes[0] + boxGoneMargin
+
+        val box2Left = (rootSize - boxSizes[2]) * 0.5f
+        val box2Top = box1Top + boxSizes[1] + boxMargin
+
+        rule.onNodeWithTag("box0").assertPositionInRootIsEqualTo(box0Left, box0Top)
+        rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(box1Left, box1Top)
+        rule.onNodeWithTag("box2").assertPositionInRootIsEqualTo(box2Left, box2Top)
+    }
+
+    @Test
+    fun testHorizontalWeight_withConstraintSet() {
+        val rootSize = 100.dp
+        val boxSize = 10.dp
+
+        rule.setContent {
+            ConstraintLayout(
+                modifier = Modifier
+                    .background(Color.LightGray)
+                    .size(rootSize),
+                constraintSet = ConstraintSet {
+                    val box0 = createRefFor("box0")
+                    val box1 = createRefFor("box1")
+
+                    constrain(box0) {
+                        width = Dimension.fillToConstraints
+                        height = Dimension.value(boxSize)
+
+                        horizontalChainWeight = 1.0f
+                        verticalChainWeight = 2.0f // Ignored in horizontal chain
+                    }
+                    constrain(box1) {
+                        width = Dimension.fillToConstraints
+                        height = Dimension.value(boxSize)
+                    }
+                    createHorizontalChain(box0, box1.withChainParams(weight = 0.5f))
+                }
+            ) {
+                Box(
+                    modifier = Modifier
+                        .background(Color.Red)
+                        .layoutTestId("box0")
+                )
+                Box(
+                    modifier = Modifier
+                        .background(Color.Blue)
+                        .layoutTestId("box1")
+                )
+            }
+        }
+        rule.waitForIdle()
+        rule.onNodeWithTag("box0").assertWidthIsEqualTo(rootSize * 0.667f)
+        rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(rootSize * 0.667f, 0.dp)
+        rule.onNodeWithTag("box1").assertWidthIsEqualTo(rootSize * 0.334f)
+    }
+
+    @Test
+    fun testVerticalWeight_withConstraintSet() {
+        val rootSize = 100.dp
+        val boxSize = 10.dp
+
+        rule.setContent {
+            ConstraintLayout(
+                modifier = Modifier
+                    .background(Color.LightGray)
+                    .size(rootSize),
+                constraintSet = ConstraintSet {
+                    val box0 = createRefFor("box0")
+                    val box1 = createRefFor("box1")
+
+                    constrain(box0) {
+                        width = Dimension.value(boxSize)
+                        height = Dimension.fillToConstraints
+
+                        horizontalChainWeight = 2.0f // Ignored in vertical chain
+                        verticalChainWeight = 1.0f
+                    }
+                    constrain(box1) {
+                        width = Dimension.value(boxSize)
+                        height = Dimension.fillToConstraints
+                    }
+                    createVerticalChain(box0, box1.withChainParams(weight = 0.5f))
+                }
+            ) {
+                Box(
+                    modifier = Modifier
+                        .background(Color.Red)
+                        .layoutTestId("box0")
+                )
+                Box(
+                    modifier = Modifier
+                        .background(Color.Blue)
+                        .layoutTestId("box1")
+                )
+            }
+        }
+        rule.waitForIdle()
+        rule.onNodeWithTag("box0").assertHeightIsEqualTo(rootSize * 0.667f)
+        rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(0.dp, rootSize * 0.667f)
+        rule.onNodeWithTag("box1").assertHeightIsEqualTo(rootSize * 0.334f)
+    }
 }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
index 5c2b507..c93b13f 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
@@ -1663,4 +1663,68 @@
         rule.onNodeWithTag(boxTag1).assertPositionInRootIsEqualTo(29.5.dp, 0.dp)
         rule.onNodeWithTag(boxTag2).assertPositionInRootIsEqualTo(60.dp, 0.dp)
     }
+
+    @Test
+    fun testBias_withConstraintSet() {
+        val rootSize = 100.dp
+        val boxSize = 10.dp
+        val horBias = 0.2f
+        val verBias = 1f - horBias
+        rule.setContent {
+            ConstraintLayout(
+                modifier = Modifier.size(rootSize),
+                constraintSet = ConstraintSet {
+                    constrain(createRefFor("box")) {
+                        width = Dimension.value(boxSize)
+                        height = Dimension.value(boxSize)
+
+                        centerTo(parent)
+                        horizontalBias = horBias
+                        verticalBias = verBias
+                    }
+                }) {
+                Box(
+                    modifier = Modifier
+                        .background(Color.Red)
+                        .layoutTestId("box")
+                )
+            }
+        }
+        rule.waitForIdle()
+        rule.onNodeWithTag("box").assertPositionInRootIsEqualTo(
+            (rootSize - boxSize) * 0.2f,
+            (rootSize - boxSize) * 0.8f
+        )
+    }
+
+    @Test
+    fun testBias_withInlineDsl() {
+        val rootSize = 100.dp
+        val boxSize = 10.dp
+        val horBias = 0.2f
+        val verBias = 1f - horBias
+        rule.setContent {
+            ConstraintLayout(Modifier.size(rootSize)) {
+                val box = createRef()
+                Box(
+                    modifier = Modifier
+                        .background(Color.Red)
+                        .constrainAs(box) {
+                            width = Dimension.value(boxSize)
+                            height = Dimension.value(boxSize)
+
+                            centerTo(parent)
+                            horizontalBias = horBias
+                            verticalBias = verBias
+                        }
+                        .layoutTestId("box")
+                )
+            }
+        }
+        rule.waitForIdle()
+        rule.onNodeWithTag("box").assertPositionInRootIsEqualTo(
+            (rootSize - boxSize) * 0.2f,
+            (rootSize - boxSize) * 0.8f
+        )
+    }
 }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt
index 4e171e2..fb1c981 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt
@@ -134,7 +134,7 @@
         set(value) {
             field = value
             addTransform {
-                if (visibility != Visibility.Invisible) {
+                if (this@ConstrainScope.visibility != Visibility.Invisible) {
                     // A bit of a hack, this behavior is not defined in :core
                     // Invisible should override alpha
                     alpha(value)
@@ -260,6 +260,44 @@
             }
         }
 
+    /**
+     * Applied when the widget has constraints on the [start] and [end] anchors. It defines the
+     * position of the widget relative to the space within the constraints, where `0f` is the
+     * left-most position and `1f` is the right-most position.
+     *
+     * &nbsp;
+     *
+     * When layout direction is RTL, the value of the bias is effectively inverted.
+     *
+     * E.g.: For `horizontalBias = 0.3f`, `0.7f` is used for RTL.
+     *
+     * &nbsp;
+     *
+     * Note that the bias may also be applied with calls such as [linkTo].
+     */
+    @FloatRange(0.0, 1.0)
+    var horizontalBias: Float = 0.5f
+        set(value) {
+            field = value
+            tasks.add { state ->
+                state.constraints(id).horizontalBias(value)
+            }
+        }
+
+    /**
+     * Applied when the widget has constraints on the [top] and [bottom] anchors. It defines the
+     * position of the widget relative to the space within the constraints, where `0f` is the
+     * top-most position and `1f` is the bottom-most position.
+     */
+    @FloatRange(0.0, 1.0)
+    var verticalBias: Float = 0.5f
+        set(value) {
+            field = value
+            tasks.add { state ->
+                state.constraints(id).verticalBias(value)
+            }
+        }
+
     private fun addTransform(change: ConstraintReference.() -> Unit) =
         tasks.add { state -> change(state.constraints(id)) }
 
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
index 3193e55..d086df2 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
@@ -583,7 +583,7 @@
         updateHelpersHashCode(16)
         elements.forEach { updateHelpersHashCode(it.hashCode()) }
 
-         return ConstrainedLayoutReference(id)
+        return ConstrainedLayoutReference(id)
     }
 
     /**
@@ -592,7 +592,6 @@
      * Use [constrain] with the resulting [HorizontalChainReference] to modify the start/left and
      * end/right constraints of this chain.
      */
-    // TODO(popam, b/157783937): this API should be improved
     fun createHorizontalChain(
         vararg elements: LayoutReference,
         chainStyle: ChainStyle = ChainStyle.Spread
@@ -603,7 +602,17 @@
                 id,
                 androidx.constraintlayout.core.state.State.Helper.HORIZONTAL_CHAIN
             ) as androidx.constraintlayout.core.state.helpers.HorizontalChainReference
-            helper.add(*(elements.map { it.id }.toTypedArray()))
+            elements.forEach { chainElement ->
+                val elementParams = chainElement.getHelperParams() ?: ChainParams.Default
+                helper.addChainElement(
+                    chainElement.id,
+                    elementParams.weight,
+                    state.convertDimension(elementParams.startMargin).toFloat(),
+                    state.convertDimension(elementParams.endMargin).toFloat(),
+                    state.convertDimension(elementParams.startGoneMargin).toFloat(),
+                    state.convertDimension(elementParams.endGoneMargin).toFloat()
+                )
+            }
             helper.style(chainStyle.style)
             helper.apply()
             if (chainStyle.bias != null) {
@@ -622,7 +631,6 @@
      * Use [constrain] with the resulting [VerticalChainReference] to modify the top and
      * bottom constraints of this chain.
      */
-    // TODO(popam, b/157783937): this API should be improved
     fun createVerticalChain(
         vararg elements: LayoutReference,
         chainStyle: ChainStyle = ChainStyle.Spread
@@ -633,7 +641,17 @@
                 id,
                 androidx.constraintlayout.core.state.State.Helper.VERTICAL_CHAIN
             ) as androidx.constraintlayout.core.state.helpers.VerticalChainReference
-            helper.add(*(elements.map { it.id }.toTypedArray()))
+            elements.forEach { chainElement ->
+                val elementParams = chainElement.getHelperParams() ?: ChainParams.Default
+                helper.addChainElement(
+                    chainElement.id,
+                    elementParams.weight,
+                    state.convertDimension(elementParams.topMargin).toFloat(),
+                    state.convertDimension(elementParams.bottomMargin).toFloat(),
+                    state.convertDimension(elementParams.topGoneMargin).toFloat(),
+                    state.convertDimension(elementParams.bottomGoneMargin).toFloat()
+                )
+            }
             helper.style(chainStyle.style)
             helper.apply()
             if (chainStyle.bias != null) {
@@ -645,6 +663,147 @@
         updateHelpersHashCode(chainStyle.hashCode())
         return VerticalChainReference(id)
     }
+
+    /**
+     * Sets the parameters that are used by chains to customize the resulting layout.
+     *
+     * Use margins to customize the space between widgets in the chain.
+     *
+     * Use weight to distribute available space to each widget when their dimensions are not
+     * fixed.
+     *
+     * &nbsp;
+     *
+     * Similarly named parameters available from [ConstrainScope.linkTo] are ignored in
+     * Chains.
+     *
+     * Since margins are only for widgets within the chain: Top, Start and End, Bottom margins are
+     * ignored when the widget is the first or the last element in the chain, respectively.
+     *
+     * @param startMargin Added space from the start of this widget to the previous widget
+     * @param topMargin Added space from the top of this widget to the previous widget
+     * @param endMargin Added space from the end of this widget to the next widget
+     * @param bottomMargin Added space from the bottom of this widget to the next widget
+     * @param startGoneMargin Added space from the start of this widget when the previous widget has [Visibility.Gone]
+     * @param topGoneMargin Added space from the top of this widget when the previous widget has [Visibility.Gone]
+     * @param endGoneMargin Added space from the end of this widget when the next widget has [Visibility.Gone]
+     * @param bottomGoneMargin Added space from the bottom of this widget when the next widget has [Visibility.Gone]
+     * @param weight Defines the proportion of space (relative to the total weight) occupied by this
+     * layout when the corresponding dimension is not a fixed value.
+     * @return The same [LayoutReference] instance with the applied values
+     */
+    fun LayoutReference.withChainParams(
+        startMargin: Dp = 0.dp,
+        topMargin: Dp = 0.dp,
+        endMargin: Dp = 0.dp,
+        bottomMargin: Dp = 0.dp,
+        startGoneMargin: Dp = 0.dp,
+        topGoneMargin: Dp = 0.dp,
+        endGoneMargin: Dp = 0.dp,
+        bottomGoneMargin: Dp = 0.dp,
+        weight: Float = Float.NaN,
+    ): LayoutReference =
+        this.apply {
+            setHelperParams(
+                ChainParams(
+                    startMargin = startMargin,
+                    topMargin = topMargin,
+                    endMargin = endMargin,
+                    bottomMargin = bottomMargin,
+                    startGoneMargin = startGoneMargin,
+                    topGoneMargin = topGoneMargin,
+                    endGoneMargin = endGoneMargin,
+                    bottomGoneMargin = bottomGoneMargin,
+                    weight = weight
+                )
+            )
+        }
+
+    /**
+     * Sets the parameters that are used by horizontal chains to customize the resulting layout.
+     *
+     * Use margins to customize the space between widgets in the chain.
+     *
+     * Use weight to distribute available space to each widget when their horizontal dimension is
+     * not fixed.
+     *
+     * &nbsp;
+     *
+     * Similarly named parameters available from [ConstrainScope.linkTo] are ignored in
+     * Chains.
+     *
+     * Since margins are only for widgets within the chain: Start and End margins are
+     * ignored when the widget is the first or the last element in the chain, respectively.
+     *
+     * @param startMargin Added space from the start of this widget to the previous widget
+     * @param endMargin Added space from the end of this widget to the next widget
+     * @param startGoneMargin Added space from the start of this widget when the previous widget has [Visibility.Gone]
+     * @param endGoneMargin Added space from the end of this widget when the next widget has [Visibility.Gone]
+     * @param weight Defines the proportion of space (relative to the total weight) occupied by this
+     * layout when the width is not a fixed dimension.
+     * @return The same [LayoutReference] instance with the applied values
+     */
+    fun LayoutReference.withHorizontalChainParams(
+        startMargin: Dp = 0.dp,
+        endMargin: Dp = 0.dp,
+        startGoneMargin: Dp = 0.dp,
+        endGoneMargin: Dp = 0.dp,
+        weight: Float = Float.NaN
+    ): LayoutReference =
+        withChainParams(
+            startMargin = startMargin,
+            topMargin = 0.dp,
+            endMargin = endMargin,
+            bottomMargin = 0.dp,
+            startGoneMargin = startGoneMargin,
+            topGoneMargin = 0.dp,
+            endGoneMargin = endGoneMargin,
+            bottomGoneMargin = 0.dp,
+            weight = weight
+        )
+
+    /**
+     * Sets the parameters that are used by vertical chains to customize the resulting layout.
+     *
+     * Use margins to customize the space between widgets in the chain.
+     *
+     * Use weight to distribute available space to each widget when their vertical dimension is not
+     * fixed.
+     *
+     * &nbsp;
+     *
+     * Similarly named parameters available from [ConstrainScope.linkTo] are ignored in
+     * Chains.
+     *
+     * Since margins are only for widgets within the chain: Top and Bottom margins are
+     * ignored when the widget is the first or the last element in the chain, respectively.
+     *
+     * @param topMargin Added space from the top of this widget to the previous widget
+     * @param bottomMargin Added space from the bottom of this widget to the next widget
+     * @param topGoneMargin Added space from the top of this widget when the previous widget has [Visibility.Gone]
+     * @param bottomGoneMargin Added space from the bottom of this widget when the next widget has [Visibility.Gone]
+     * @param weight Defines the proportion of space (relative to the total weight) occupied by this
+     * layout when the height is not a fixed dimension.
+     * @return The same [LayoutReference] instance with the applied values
+     */
+    fun LayoutReference.withVerticalChainParams(
+        topMargin: Dp = 0.dp,
+        bottomMargin: Dp = 0.dp,
+        topGoneMargin: Dp = 0.dp,
+        bottomGoneMargin: Dp = 0.dp,
+        weight: Float = Float.NaN
+    ): LayoutReference =
+        withChainParams(
+            startMargin = 0.dp,
+            topMargin = topMargin,
+            endMargin = 0.dp,
+            bottomMargin = bottomMargin,
+            startGoneMargin = 0.dp,
+            topGoneMargin = topGoneMargin,
+            endGoneMargin = 0.dp,
+            bottomGoneMargin = bottomGoneMargin,
+            weight = weight
+        )
 }
 
 /**
@@ -653,6 +812,11 @@
  */
 @Stable
 abstract class LayoutReference internal constructor(internal open val id: Any) {
+    /**
+     * This map should be used to store one instance of different implementations of [HelperParams].
+     */
+    private val helperParamsMap: MutableMap<String, HelperParams> = mutableMapOf()
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (javaClass != other?.javaClass) return false
@@ -667,6 +831,60 @@
     override fun hashCode(): Int {
         return id.hashCode()
     }
+
+    internal fun setHelperParams(helperParams: HelperParams) {
+        // Use the class name to force one instance per implementation
+        helperParamsMap[helperParams.javaClass.simpleName] = helperParams
+    }
+
+    /**
+     * Returns the [HelperParams] that corresponds to the class type [T]. Null if no instance of
+     * type [T] has been set.
+     */
+    internal inline fun <reified T> getHelperParams(): T? where T : HelperParams {
+        return helperParamsMap[T::class.java.simpleName] as? T
+    }
+}
+
+/**
+ * Helpers that need parameters on a per-widget basis may implement this interface to store custom
+ * parameters within [LayoutReference].
+ *
+ * @see [LayoutReference.getHelperParams]
+ * @see [LayoutReference.setHelperParams]
+ */
+internal interface HelperParams
+
+/**
+ * Parameters that may be defined for each widget within a chain.
+ *
+ * These will always be used instead of similarly named parameters defined with other calls such as
+ * [ConstrainScope.linkTo].
+ */
+internal class ChainParams(
+    val startMargin: Dp,
+    val topMargin: Dp,
+    val endMargin: Dp,
+    val bottomMargin: Dp,
+    val startGoneMargin: Dp,
+    val topGoneMargin: Dp,
+    val endGoneMargin: Dp,
+    val bottomGoneMargin: Dp,
+    val weight: Float,
+) : HelperParams {
+    companion object {
+        internal val Default = ChainParams(
+            startMargin = 0.dp,
+            topMargin = 0.dp,
+            endMargin = 0.dp,
+            bottomMargin = 0.dp,
+            startGoneMargin = 0.dp,
+            topGoneMargin = 0.dp,
+            endGoneMargin = 0.dp,
+            bottomGoneMargin = 0.dp,
+            weight = Float.NaN
+        )
+    }
 }
 
 /**
@@ -859,7 +1077,7 @@
 @Immutable
 class Wrap internal constructor(
     internal val mode: Int
-    ) {
+) {
     companion object {
         val None =
             Wrap(androidx.constraintlayout.core.widgets.Flow.WRAP_NONE)
@@ -876,7 +1094,7 @@
 @Immutable
 class VerticalAlign internal constructor(
     internal val mode: Int
-    ) {
+) {
     companion object {
         val Top = VerticalAlign(androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_TOP)
         val Bottom =
@@ -894,7 +1112,7 @@
 @Immutable
 class HorizontalAlign internal constructor(
     internal val mode: Int
-    ) {
+) {
     companion object {
         val Start =
             HorizontalAlign(androidx.constraintlayout.core.widgets.Flow.HORIZONTAL_ALIGN_START)
@@ -910,7 +1128,7 @@
 @Immutable
 class FlowStyle internal constructor(
     internal val style: Int
-    ) {
+) {
     companion object {
         val Spread = FlowStyle(0)
         val SpreadInside = FlowStyle(1)
diff --git a/constraintlayout/constraintlayout-core/api/current.txt b/constraintlayout/constraintlayout-core/api/current.txt
index ff1bb30..30a9d6c 100644
--- a/constraintlayout/constraintlayout-core/api/current.txt
+++ b/constraintlayout/constraintlayout-core/api/current.txt
@@ -2477,20 +2477,22 @@
   }
 
   public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public ChainReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public void addChainElement(String!, float, float, float);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference! bias(float);
+    ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
+    method public void addChainElement(String, float, float, float);
+    method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
     method public float getBias();
-    method protected float getPostMargin(String!);
-    method protected float getPreMargin(String!);
-    method public androidx.constraintlayout.core.state.State.Chain! getStyle();
-    method protected float getWeight(String!);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference! style(androidx.constraintlayout.core.state.State.Chain!);
+    method protected float getPostGoneMargin(String);
+    method protected float getPostMargin(String);
+    method protected float getPreGoneMargin(String);
+    method protected float getPreMargin(String);
+    method public androidx.constraintlayout.core.state.State.Chain getStyle();
+    method protected float getWeight(String);
+    method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
     field protected float mBias;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
-    field protected androidx.constraintlayout.core.state.State.Chain! mStyle;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
+    field protected androidx.constraintlayout.core.state.State.Chain mStyle;
   }
 
   public interface Facade {
diff --git a/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
index ff1bb30..30a9d6c 100644
--- a/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
@@ -2477,20 +2477,22 @@
   }
 
   public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public ChainReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public void addChainElement(String!, float, float, float);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference! bias(float);
+    ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
+    method public void addChainElement(String, float, float, float);
+    method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
     method public float getBias();
-    method protected float getPostMargin(String!);
-    method protected float getPreMargin(String!);
-    method public androidx.constraintlayout.core.state.State.Chain! getStyle();
-    method protected float getWeight(String!);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference! style(androidx.constraintlayout.core.state.State.Chain!);
+    method protected float getPostGoneMargin(String);
+    method protected float getPostMargin(String);
+    method protected float getPreGoneMargin(String);
+    method protected float getPreMargin(String);
+    method public androidx.constraintlayout.core.state.State.Chain getStyle();
+    method protected float getWeight(String);
+    method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
     field protected float mBias;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
-    field protected androidx.constraintlayout.core.state.State.Chain! mStyle;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
+    field protected androidx.constraintlayout.core.state.State.Chain mStyle;
   }
 
   public interface Facade {
diff --git a/constraintlayout/constraintlayout-core/api/restricted_current.txt b/constraintlayout/constraintlayout-core/api/restricted_current.txt
index ff1bb30..161a4ec 100644
--- a/constraintlayout/constraintlayout-core/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-core/api/restricted_current.txt
@@ -2477,20 +2477,23 @@
   }
 
   public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public ChainReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public void addChainElement(String!, float, float, float);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference! bias(float);
+    ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
+    method public void addChainElement(String, float, float, float);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void addChainElement(Object, float, float, float, float, float);
+    method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
     method public float getBias();
-    method protected float getPostMargin(String!);
-    method protected float getPreMargin(String!);
-    method public androidx.constraintlayout.core.state.State.Chain! getStyle();
-    method protected float getWeight(String!);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference! style(androidx.constraintlayout.core.state.State.Chain!);
+    method protected float getPostGoneMargin(String);
+    method protected float getPostMargin(String);
+    method protected float getPreGoneMargin(String);
+    method protected float getPreMargin(String);
+    method public androidx.constraintlayout.core.state.State.Chain getStyle();
+    method protected float getWeight(String);
+    method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
     field protected float mBias;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
-    field protected androidx.constraintlayout.core.state.State.Chain! mStyle;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
+    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
+    field protected androidx.constraintlayout.core.state.State.Chain mStyle;
   }
 
   public interface Facade {
diff --git a/constraintlayout/constraintlayout-core/build.gradle b/constraintlayout/constraintlayout-core/build.gradle
index 40b4820..329d954 100644
--- a/constraintlayout/constraintlayout-core/build.gradle
+++ b/constraintlayout/constraintlayout-core/build.gradle
@@ -23,6 +23,7 @@
 }
 
 dependencies {
+    api("androidx.annotation:annotation:1.5.0")
     testImplementation(libs.junit)
 }
 
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
index edf122b..a6af993 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
@@ -811,6 +811,7 @@
                                         postMargin = toPix(state, array.getFloat(3));
                                         break;
                                 }
+                                // TODO: Define how to set gone margin in JSON syntax
                                 chain.addChainElement(id, weight, preMargin, postMargin);
                             }
                         } else {
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
index 4229892..5a22613 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
@@ -18,85 +18,181 @@
 
 import static androidx.constraintlayout.core.widgets.ConstraintWidget.UNKNOWN;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.constraintlayout.core.state.HelperReference;
 import androidx.constraintlayout.core.state.State;
 
 import java.util.HashMap;
 
+/**
+ * {@link HelperReference} for Chains.
+ *
+ * Elements should be added with {@link ChainReference#addChainElement}
+ */
 public class ChainReference extends HelperReference {
 
     protected float mBias = 0.5f;
-    protected HashMap<String ,Float> mMapWeights;
-    protected HashMap<String,Float> mMapPreMargin;
-    protected HashMap<String,Float> mMapPostMargin;
 
-    protected State.Chain mStyle = State.Chain.SPREAD;
+    /**
+     * @deprecated Unintended visibility, use {@link #getWeight(String)} instead
+     */
+    @Deprecated // TODO(b/253515185): Change to private visibility once we change major version
+    protected @NonNull HashMap<String, Float> mMapWeights = new HashMap<>();
 
-    public ChainReference(State state, State.Helper type) {
+    /**
+     * @deprecated Unintended visibility, use {@link #getPreMargin(String)} instead
+     */
+    @Deprecated // TODO(b/253515185): Change to private visibility once we change major version
+    protected @NonNull HashMap<String, Float> mMapPreMargin = new HashMap<>();
+
+    /**
+     * @deprecated Unintended visibility, use {@link #getPostMargin(String)} instead
+     */
+    @Deprecated // TODO(b/253515185): Change to private visibility once we change major version
+    protected @NonNull HashMap<String, Float> mMapPostMargin = new HashMap<>();
+
+    private HashMap<String, Float> mMapPreGoneMargin;
+    private HashMap<String, Float> mMapPostGoneMargin;
+
+    protected @NonNull State.Chain mStyle = State.Chain.SPREAD;
+
+    public ChainReference(@NonNull State state, @NonNull State.Helper type) {
         super(state, type);
     }
 
-    public State.Chain getStyle() {
+    public @NonNull State.Chain getStyle() {
         return State.Chain.SPREAD;
     }
 
-    // @TODO: add description
-    public ChainReference style(State.Chain style) {
+    /**
+     * Sets the {@link State.Chain style}.
+     *
+     * @param style Defines the way the chain will lay out its elements
+     * @return This same instance
+     */
+    @NonNull
+    public ChainReference style(@NonNull State.Chain style) {
         mStyle = style;
         return this;
     }
 
-    public void addChainElement(String id, float weight, float preMargin, float  postMargin ) {
-        super.add(id);
+    /**
+     * Adds the element by the given id to the Chain.
+     *
+     * The order in which the elements are added is important. It will represent the element's
+     * position in the Chain.
+     *
+     * @param id         Id of the element to add
+     * @param weight     Weight used to distribute remaining space to each element
+     * @param preMargin  Additional space in pixels between the added element and the previous one
+     *                   (if any)
+     * @param postMargin Additional space in pixels between the added element and the next one (if
+     *                   any)
+     */
+    public void addChainElement(@NonNull String id,
+            float weight,
+            float preMargin,
+            float postMargin) {
+        addChainElement(id, weight, preMargin, postMargin, 0, 0);
+    }
+
+    /**
+     * Adds the element by the given id to the Chain.
+     *
+     * The object's {@link Object#toString()} result will be used to map the given margins and
+     * weight to it, so it must stable and comparable.
+     *
+     * The order in which the elements are added is important. It will represent the element's
+     * position in the Chain.
+     *
+     * @param id             Id of the element to add
+     * @param weight         Weight used to distribute remaining space to each element
+     * @param preMargin      Additional space in pixels between the added element and the
+     *                       previous one
+     *                       (if any)
+     * @param postMargin     Additional space in pixels between the added element and the next
+     *                       one (if
+     *                       any)
+     * @param preGoneMargin  Additional space in pixels between the added element and the previous
+     *                       one (if any) when the previous element has Gone visibility
+     * @param postGoneMargin Additional space in pixels between the added element and the next
+     *                       one (if any) when the next element has Gone visibility
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public void addChainElement(@NonNull Object id,
+            float weight,
+            float preMargin,
+            float postMargin,
+            float preGoneMargin,
+            float postGoneMargin) {
+        super.add(id); // Add element id as is, it's expected to return the same given instance
+        String idString = id.toString();
         if (!Float.isNaN(weight)) {
-            if (mMapWeights == null) {
-                mMapWeights = new HashMap<>();
-            }
-            mMapWeights.put(id, weight);
+            mMapWeights.put(idString, weight);
         }
         if (!Float.isNaN(preMargin)) {
-            if (mMapPreMargin == null) {
-                mMapPreMargin = new HashMap<>();
-            }
-            mMapPreMargin.put(id, preMargin);
+            mMapPreMargin.put(idString, preMargin);
         }
         if (!Float.isNaN(postMargin)) {
-            if (mMapPostMargin == null) {
-                mMapPostMargin = new HashMap<>();
+            mMapPostMargin.put(idString, postMargin);
+        }
+        if (!Float.isNaN(preGoneMargin)) {
+            if (mMapPreGoneMargin == null) {
+                mMapPreGoneMargin = new HashMap<>();
             }
-            mMapPostMargin.put(id, postMargin);
+            mMapPreGoneMargin.put(idString, preGoneMargin);
+        }
+        if (!Float.isNaN(postGoneMargin)) {
+            if (mMapPostGoneMargin == null) {
+                mMapPostGoneMargin = new HashMap<>();
+            }
+            mMapPostGoneMargin.put(idString, postGoneMargin);
         }
     }
 
-  protected float getWeight(String id) {
-       if (mMapWeights == null) {
-           return UNKNOWN;
-       }
-       if (mMapWeights.containsKey(id)) {
-           return mMapWeights.get(id);
-       }
-       return UNKNOWN;
+    protected float getWeight(@NonNull String id) {
+        if (mMapWeights.containsKey(id)) {
+            return mMapWeights.get(id);
+        }
+        return UNKNOWN;
     }
 
-    protected float getPostMargin(String id) {
-        if (mMapPostMargin != null  && mMapPostMargin.containsKey(id)) {
+    protected float getPostMargin(@NonNull String id) {
+        if (mMapPostMargin.containsKey(id)) {
             return mMapPostMargin.get(id);
         }
         return 0;
     }
 
-    protected float getPreMargin(String id) {
-        if (mMapPreMargin != null  && mMapPreMargin.containsKey(id)) {
+    protected float getPreMargin(@NonNull String id) {
+        if (mMapPreMargin.containsKey(id)) {
             return mMapPreMargin.get(id);
         }
         return 0;
     }
 
+    protected float getPostGoneMargin(@NonNull String id) {
+        if (mMapPostGoneMargin != null && mMapPostGoneMargin.containsKey(id)) {
+            return mMapPostGoneMargin.get(id);
+        }
+        return 0;
+    }
+
+    protected float getPreGoneMargin(@NonNull String id) {
+        if (mMapPreGoneMargin != null && mMapPreGoneMargin.containsKey(id)) {
+            return mMapPreGoneMargin.get(id);
+        }
+        return 0;
+    }
+
     public float getBias() {
         return mBias;
     }
 
     // @TODO: add description
+    @NonNull
     @Override
     public ChainReference bias(float bias) {
         mBias = bias;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java
index be09236..32539da 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java
@@ -58,14 +58,17 @@
                 } else {
                     // No constraint declared, default to Parent.
                     String refKey = reference.getKey().toString();
-                    first.startToStart(State.PARENT).margin(getPreMargin(refKey));
+                    first.startToStart(State.PARENT).margin(getPreMargin(refKey)).marginGone(
+                            getPreGoneMargin(refKey));
                 }
             }
             if (previous != null) {
                 String preKey = previous.getKey().toString();
                 String refKey = reference.getKey().toString();
-                previous.endToStart(reference.getKey()).margin(getPostMargin(preKey));
-                reference.startToEnd(previous.getKey()).margin(getPreMargin(refKey));
+                previous.endToStart(reference.getKey()).margin(getPostMargin(preKey)).marginGone(
+                        getPostGoneMargin(preKey));
+                reference.startToEnd(previous.getKey()).margin(getPreMargin(refKey)).marginGone(
+                        getPreGoneMargin(refKey));
             }
             float weight = getWeight(key.toString());
             if (weight != UNKNOWN) {
@@ -88,7 +91,8 @@
             } else {
                 // No constraint declared, default to Parent.
                 String preKey = previous.getKey().toString();
-                previous.endToEnd(State.PARENT).margin(getPostMargin(preKey));
+                previous.endToEnd(State.PARENT).margin(getPostMargin(preKey)).marginGone(
+                        getPostGoneMargin(preKey));
             }
         }
 
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java
index d4fdd23..8d880b1 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java
@@ -49,14 +49,17 @@
                 } else {
                     // No constraint declared, default to Parent.
                     String refKey = reference.getKey().toString();
-                    first.topToTop(State.PARENT).margin(getPreMargin(refKey));
+                    first.topToTop(State.PARENT).margin(getPreMargin(refKey)).marginGone(
+                            getPreGoneMargin(refKey));
                 }
             }
             if (previous != null) {
                 String preKey = previous.getKey().toString();
                 String refKey = reference.getKey().toString();
-                previous.bottomToTop(reference.getKey()).margin(getPostMargin(preKey));
-                reference.topToBottom(previous.getKey()).margin(getPreMargin(refKey));
+                previous.bottomToTop(reference.getKey()).margin(getPostMargin(preKey)).marginGone(
+                        getPostGoneMargin(preKey));
+                reference.topToBottom(previous.getKey()).margin(getPreMargin(refKey)).marginGone(
+                        getPreGoneMargin(refKey));
             }
             float weight = getWeight(key.toString());
             if (weight != UNKNOWN) {
@@ -77,7 +80,8 @@
             } else {
                 // No constraint declared, default to Parent.
                 String preKey = previous.getKey().toString();
-                previous.bottomToBottom(State.PARENT).margin(getPostMargin(preKey));
+                previous.bottomToBottom(State.PARENT).margin(getPostMargin(preKey)).marginGone(
+                        getPostGoneMargin(preKey));
             }
         }
 
diff --git a/credentials/credentials/api/current.txt b/credentials/credentials/api/current.txt
index 3544ddf9..b14a46d 100644
--- a/credentials/credentials/api/current.txt
+++ b/credentials/credentials/api/current.txt
@@ -1,12 +1,22 @@
 // Signature format: 4.0
 package androidx.credentials {
 
-  public abstract class CreateCredentialRequest {
-    ctor public CreateCredentialRequest();
+  public class CreateCredentialRequest {
+    ctor public CreateCredentialRequest(String type, android.os.Bundle data, boolean requireSystemProvider);
+    method public final android.os.Bundle getData();
+    method public final boolean getRequireSystemProvider();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final boolean requireSystemProvider;
+    property public final String type;
   }
 
-  public abstract class CreateCredentialResponse {
-    ctor public CreateCredentialResponse();
+  public class CreateCredentialResponse {
+    ctor public CreateCredentialResponse(String type, android.os.Bundle data);
+    method public final android.os.Bundle getData();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final String type;
   }
 
   public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
@@ -21,8 +31,12 @@
     ctor public CreatePasswordResponse();
   }
 
-  public abstract class Credential {
-    ctor public Credential();
+  public class Credential {
+    ctor public Credential(String type, android.os.Bundle data);
+    method public final android.os.Bundle getData();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final String type;
   }
 
   public final class CredentialManager {
@@ -51,8 +65,14 @@
     property public final CharSequence? errorMessage;
   }
 
-  public abstract class GetCredentialOption {
-    ctor public GetCredentialOption();
+  public class GetCredentialOption {
+    ctor public GetCredentialOption(String type, android.os.Bundle data, boolean requireSystemProvider);
+    method public final android.os.Bundle getData();
+    method public final boolean getRequireSystemProvider();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final boolean requireSystemProvider;
+    property public final String type;
   }
 
   public final class GetCredentialRequest {
diff --git a/credentials/credentials/api/public_plus_experimental_current.txt b/credentials/credentials/api/public_plus_experimental_current.txt
index 3544ddf9..b14a46d 100644
--- a/credentials/credentials/api/public_plus_experimental_current.txt
+++ b/credentials/credentials/api/public_plus_experimental_current.txt
@@ -1,12 +1,22 @@
 // Signature format: 4.0
 package androidx.credentials {
 
-  public abstract class CreateCredentialRequest {
-    ctor public CreateCredentialRequest();
+  public class CreateCredentialRequest {
+    ctor public CreateCredentialRequest(String type, android.os.Bundle data, boolean requireSystemProvider);
+    method public final android.os.Bundle getData();
+    method public final boolean getRequireSystemProvider();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final boolean requireSystemProvider;
+    property public final String type;
   }
 
-  public abstract class CreateCredentialResponse {
-    ctor public CreateCredentialResponse();
+  public class CreateCredentialResponse {
+    ctor public CreateCredentialResponse(String type, android.os.Bundle data);
+    method public final android.os.Bundle getData();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final String type;
   }
 
   public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
@@ -21,8 +31,12 @@
     ctor public CreatePasswordResponse();
   }
 
-  public abstract class Credential {
-    ctor public Credential();
+  public class Credential {
+    ctor public Credential(String type, android.os.Bundle data);
+    method public final android.os.Bundle getData();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final String type;
   }
 
   public final class CredentialManager {
@@ -51,8 +65,14 @@
     property public final CharSequence? errorMessage;
   }
 
-  public abstract class GetCredentialOption {
-    ctor public GetCredentialOption();
+  public class GetCredentialOption {
+    ctor public GetCredentialOption(String type, android.os.Bundle data, boolean requireSystemProvider);
+    method public final android.os.Bundle getData();
+    method public final boolean getRequireSystemProvider();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final boolean requireSystemProvider;
+    property public final String type;
   }
 
   public final class GetCredentialRequest {
diff --git a/credentials/credentials/api/restricted_current.txt b/credentials/credentials/api/restricted_current.txt
index 3544ddf9..b14a46d 100644
--- a/credentials/credentials/api/restricted_current.txt
+++ b/credentials/credentials/api/restricted_current.txt
@@ -1,12 +1,22 @@
 // Signature format: 4.0
 package androidx.credentials {
 
-  public abstract class CreateCredentialRequest {
-    ctor public CreateCredentialRequest();
+  public class CreateCredentialRequest {
+    ctor public CreateCredentialRequest(String type, android.os.Bundle data, boolean requireSystemProvider);
+    method public final android.os.Bundle getData();
+    method public final boolean getRequireSystemProvider();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final boolean requireSystemProvider;
+    property public final String type;
   }
 
-  public abstract class CreateCredentialResponse {
-    ctor public CreateCredentialResponse();
+  public class CreateCredentialResponse {
+    ctor public CreateCredentialResponse(String type, android.os.Bundle data);
+    method public final android.os.Bundle getData();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final String type;
   }
 
   public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
@@ -21,8 +31,12 @@
     ctor public CreatePasswordResponse();
   }
 
-  public abstract class Credential {
-    ctor public Credential();
+  public class Credential {
+    ctor public Credential(String type, android.os.Bundle data);
+    method public final android.os.Bundle getData();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final String type;
   }
 
   public final class CredentialManager {
@@ -51,8 +65,14 @@
     property public final CharSequence? errorMessage;
   }
 
-  public abstract class GetCredentialOption {
-    ctor public GetCredentialOption();
+  public class GetCredentialOption {
+    ctor public GetCredentialOption(String type, android.os.Bundle data, boolean requireSystemProvider);
+    method public final android.os.Bundle getData();
+    method public final boolean getRequireSystemProvider();
+    method public final String getType();
+    property public final android.os.Bundle data;
+    property public final boolean requireSystemProvider;
+    property public final String type;
   }
 
   public final class GetCredentialRequest {
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index 29d733e..6bb5a52 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -24,6 +24,7 @@
 }
 
 dependencies {
+    api("androidx.annotation:annotation:1.5.0")
     api(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesCore)
 
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
new file mode 100644
index 0000000..953bd5a
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePasswordRequestJavaTest {
+    @Test
+    public void constructor_nullId_throws() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new CreatePasswordRequest(null, "pwd")
+        );
+    }
+
+    @Test
+    public void constructor_nullPassword_throws() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new CreatePasswordRequest("id", null)
+        );
+    }
+
+    @Test
+    public void constructor_emptyPassword_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new CreatePasswordRequest("id", "")
+        );
+    }
+
+    @Test
+    public void getter_id() {
+        String idExpected = "id";
+        CreatePasswordRequest request = new CreatePasswordRequest(idExpected, "password");
+        assertThat(request.getId()).isEqualTo(idExpected);
+    }
+
+    @Test
+    public void getter_password() {
+        String passwordExpected = "pwd";
+        CreatePasswordRequest request = new CreatePasswordRequest("id", passwordExpected);
+        assertThat(request.getPassword()).isEqualTo(passwordExpected);
+    }
+
+    @Test
+    public void getter_frameworkProperties() {
+        String idExpected = "id";
+        String passwordExpected = "pwd";
+        Bundle expectedData = new Bundle();
+        expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected);
+        expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_PASSWORD, passwordExpected);
+
+        CreatePasswordRequest request = new CreatePasswordRequest(idExpected, passwordExpected);
+
+        assertThat(request.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(request.getData(), expectedData)).isTrue();
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
new file mode 100644
index 0000000..9943eed
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 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.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePasswordRequestTest {
+    @Test
+    fun constructor_emptyPassword_throws() {
+        assertThrows<IllegalArgumentException> {
+            CreatePasswordRequest("id", "")
+        }
+    }
+
+    @Test
+    fun getter_id() {
+        val idExpected = "id"
+        val request = CreatePasswordRequest(idExpected, "password")
+        assertThat(request.id).isEqualTo(idExpected)
+    }
+
+    @Test
+    fun getter_password() {
+        val passwordExpected = "pwd"
+        val request = CreatePasswordRequest("id", passwordExpected)
+        assertThat(request.password).isEqualTo(passwordExpected)
+    }
+
+    @Test
+    fun getter_frameworkProperties() {
+        val idExpected = "id"
+        val passwordExpected = "pwd"
+        val expectedData = Bundle()
+        expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected)
+        expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_PASSWORD, passwordExpected)
+
+        val request = CreatePasswordRequest(idExpected, passwordExpected)
+
+        assertThat(request.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+        assertThat(equals(request.data, expectedData)).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseJavaTest.java
new file mode 100644
index 0000000..5f75653
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseJavaTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePasswordResponseJavaTest {
+    @Test
+    public void getter_frameworkProperties() {
+        CreatePasswordResponse response = new CreatePasswordResponse();
+
+        assertThat(response.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(response.getData(), Bundle.EMPTY)).isTrue();
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseTest.kt
new file mode 100644
index 0000000..84e4102
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 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.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePasswordResponseTest {
+    @Test
+    fun getter_frameworkProperties() {
+        val response = CreatePasswordResponse()
+        Truth.assertThat(response.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+        Truth.assertThat(equals(response.data, Bundle.EMPTY)).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
index db1a2a9..2604d86 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
@@ -16,10 +16,15 @@
 
 package androidx.credentials;
 
+import static androidx.credentials.CreatePublicKeyCredentialBaseRequest.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_ALLOW_HYBRID;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
 
+import android.os.Bundle;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -80,4 +85,22 @@
         String testJsonActual = createPublicKeyCredentialReq.getRequestJson();
         assertThat(testJsonActual).isEqualTo(testJsonExpected);
     }
+
+    @Test
+    public void getter_frameworkProperties_success() {
+        String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        boolean allowHybridExpected = false;
+        Bundle expectedData = new Bundle();
+        expectedData.putString(
+                BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+        expectedData.putBoolean(
+                BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+        CreatePublicKeyCredentialRequest request =
+                new CreatePublicKeyCredentialRequest(requestJsonExpected, allowHybridExpected);
+
+        assertThat(request.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(request.getData(), expectedData)).isTrue();
+        assertThat(request.getRequireSystemProvider()).isFalse();
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
index 82adc07..8558cbb 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
@@ -16,8 +16,15 @@
 
 package androidx.credentials;
 
+import static androidx.credentials.CreatePublicKeyCredentialBaseRequest.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_ALLOW_HYBRID;
+import static androidx.credentials.CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH;
+import static androidx.credentials.CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_RP;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Bundle;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -107,4 +114,26 @@
                 createPublicKeyCredentialRequestPrivileged.getClientDataHash();
         assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected);
     }
+
+    @Test
+    public void getter_frameworkProperties_success() {
+        String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        String rpExpected = "RP";
+        String clientDataHashExpected = "X342%4dfd7&";
+        boolean allowHybridExpected = false;
+        Bundle expectedData = new Bundle();
+        expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+        expectedData.putString(BUNDLE_KEY_RP, rpExpected);
+        expectedData.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHashExpected);
+        expectedData.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+        CreatePublicKeyCredentialRequestPrivileged request =
+                new CreatePublicKeyCredentialRequestPrivileged(
+                        requestJsonExpected, rpExpected, clientDataHashExpected,
+                        allowHybridExpected);
+
+        assertThat(request.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(request.getData(), expectedData)).isTrue();
+        assertThat(request.getRequireSystemProvider()).isFalse();
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
index faffbcb..e485003 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.credentials
 
+import android.os.Bundle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -110,4 +111,37 @@
         val clientDataHashActual = createPublicKeyCredentialRequestPrivileged.clientDataHash
         assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected)
     }
+
+    @Test
+    fun getter_frameworkProperties_success() {
+        val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val rpExpected = "RP"
+        val clientDataHashExpected = "X342%4dfd7&"
+        val allowHybridExpected = false
+        val expectedData = Bundle()
+        expectedData.putString(
+            CreatePublicKeyCredentialBaseRequest.BUNDLE_KEY_REQUEST_JSON,
+            requestJsonExpected
+        )
+        expectedData.putString(CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_RP, rpExpected)
+        expectedData.putString(
+            CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH,
+            clientDataHashExpected
+        )
+        expectedData.putBoolean(
+            CreatePublicKeyCredentialRequest.BUNDLE_KEY_ALLOW_HYBRID,
+            allowHybridExpected
+        )
+
+        val request = CreatePublicKeyCredentialRequestPrivileged(
+            requestJsonExpected,
+            rpExpected,
+            clientDataHashExpected,
+            allowHybridExpected
+        )
+
+        assertThat(request.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+        assertThat(equals(request.data, expectedData)).isTrue()
+        assertThat(request.requireSystemProvider).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
index 4058526..32d4365 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.credentials.CreatePublicKeyCredentialBaseRequest.Companion.BUNDLE_KEY_REQUEST_JSON
+import androidx.credentials.CreatePublicKeyCredentialRequest.Companion.BUNDLE_KEY_ALLOW_HYBRID
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -69,4 +72,26 @@
         val testJsonActual = createPublicKeyCredentialReq.requestJson
         assertThat(testJsonActual).isEqualTo(testJsonExpected)
     }
+
+    @Test
+    fun getter_frameworkProperties_success() {
+        val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val allowHybridExpected = false
+        val expectedData = Bundle()
+        expectedData.putString(
+            BUNDLE_KEY_REQUEST_JSON, requestJsonExpected
+        )
+        expectedData.putBoolean(
+            BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected
+        )
+
+        val request = CreatePublicKeyCredentialRequest(
+            requestJsonExpected,
+            allowHybridExpected
+        )
+
+        assertThat(request.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+        assertThat(equals(request.data, expectedData)).isTrue()
+        assertThat(request.requireSystemProvider).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
index dc7bdb26..099f367 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
@@ -20,6 +20,8 @@
 
 import static org.junit.Assert.assertThrows;
 
+import android.os.Bundle;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -59,4 +61,19 @@
         String testJsonActual = createPublicKeyCredentialResponse.getRegistrationResponseJson();
         assertThat(testJsonActual).isEqualTo(testJsonExpected);
     }
+
+    @Test
+    public void getter_frameworkProperties_success() {
+        String registrationResponseJsonExpected = "{\"input\":5}";
+        Bundle expectedData = new Bundle();
+        expectedData.putString(
+                CreatePublicKeyCredentialResponse.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON,
+                registrationResponseJsonExpected);
+
+        CreatePublicKeyCredentialResponse response =
+                new CreatePublicKeyCredentialResponse(registrationResponseJsonExpected);
+
+        assertThat(response.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(response.getData(), expectedData)).isTrue();
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
index 9311d59..e2179b9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.credentials
 
+import android.os.Bundle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -47,4 +48,19 @@
         val testJsonActual = createPublicKeyCredentialResponse.registrationResponseJson
         assertThat(testJsonActual).isEqualTo(testJsonExpected)
     }
+
+    @Test
+    fun getter_frameworkProperties_success() {
+        val registrationResponseJsonExpected = "{\"input\":5}"
+        val expectedData = Bundle()
+        expectedData.putString(
+            CreatePublicKeyCredentialResponse.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON,
+            registrationResponseJsonExpected
+        )
+
+        val response = CreatePublicKeyCredentialResponse(registrationResponseJsonExpected)
+
+        assertThat(response.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+        assertThat(equals(response.data, expectedData)).isTrue()
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
new file mode 100644
index 0000000..d8050da
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class GetPasswordOptionJavaTest {
+    @Test
+    public void getter_frameworkProperties() {
+        GetPasswordOption option = new GetPasswordOption();
+
+        assertThat(option.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(option.getData(), Bundle.EMPTY)).isTrue();
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
new file mode 100644
index 0000000..2c155af
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 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.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GetPasswordOptionTest {
+    @Test
+    fun getter_frameworkProperties() {
+        val option = GetPasswordOption()
+        Truth.assertThat(option.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+        Truth.assertThat(equals(option.data, Bundle.EMPTY)).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
index ec3083c..7575d2a 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
@@ -16,10 +16,15 @@
 
 package androidx.credentials;
 
+import static androidx.credentials.GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.GetPublicKeyCredentialOption.BUNDLE_KEY_ALLOW_HYBRID;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
 
+import android.os.Bundle;
+
 import org.junit.Test;
 
 public class GetPublicKeyCredentialOptionJavaTest {
@@ -73,4 +78,20 @@
         String testJsonActual = getPublicKeyCredentialOpt.getRequestJson();
         assertThat(testJsonActual).isEqualTo(testJsonExpected);
     }
+
+    @Test
+    public void getter_frameworkProperties_success() {
+        String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        boolean allowHybridExpected = false;
+        Bundle expectedData = new Bundle();
+        expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+        expectedData.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+        GetPublicKeyCredentialOption option =
+                new GetPublicKeyCredentialOption(requestJsonExpected, allowHybridExpected);
+
+        assertThat(option.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(option.getData(), expectedData)).isTrue();
+        assertThat(option.getRequireSystemProvider()).isFalse();
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
index 2b18b0d..54efd71 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
@@ -16,8 +16,15 @@
 
 package androidx.credentials;
 
+import static androidx.credentials.GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_ALLOW_HYBRID;
+import static androidx.credentials.GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH;
+import static androidx.credentials.GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_RP;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Bundle;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -106,4 +113,26 @@
         String clientDataHashActual = getPublicKeyCredentialOptionPrivileged.getClientDataHash();
         assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected);
     }
+
+    @Test
+    public void getter_frameworkProperties_success() {
+        String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        String rpExpected = "RP";
+        String clientDataHashExpected = "X342%4dfd7&";
+        boolean allowHybridExpected = false;
+        Bundle expectedData = new Bundle();
+        expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+        expectedData.putString(BUNDLE_KEY_RP, rpExpected);
+        expectedData.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHashExpected);
+        expectedData.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+        GetPublicKeyCredentialOptionPrivileged option =
+                new GetPublicKeyCredentialOptionPrivileged(
+                        requestJsonExpected, rpExpected, clientDataHashExpected,
+                        allowHybridExpected);
+
+        assertThat(option.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(option.getData(), expectedData)).isTrue();
+        assertThat(option.getRequireSystemProvider()).isFalse();
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
index 024070d..e1c1079 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.credentials
 
+import android.os.Bundle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -107,4 +108,35 @@
         val clientDataHashActual = getPublicKeyCredentialOptionPrivileged.clientDataHash
         assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected)
     }
+
+    @Test
+    fun getter_frameworkProperties_success() {
+        val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val rpExpected = "RP"
+        val clientDataHashExpected = "X342%4dfd7&"
+        val allowHybridExpected = false
+        val expectedData = Bundle()
+        expectedData.putString(
+            GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON,
+            requestJsonExpected
+        )
+        expectedData.putString(GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_RP, rpExpected)
+        expectedData.putString(
+            GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH,
+            clientDataHashExpected
+        )
+        expectedData.putBoolean(
+            GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_ALLOW_HYBRID,
+            allowHybridExpected
+        )
+
+        val option = GetPublicKeyCredentialOptionPrivileged(
+            requestJsonExpected, rpExpected, clientDataHashExpected,
+            allowHybridExpected
+        )
+
+        assertThat(option.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+        assertThat(equals(option.data, expectedData)).isTrue()
+        assertThat(option.requireSystemProvider).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
index 7cc8392..821b580 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.credentials
 
+import android.os.Bundle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -68,4 +69,25 @@
         val testJsonActual = createPublicKeyCredentialReq.requestJson
         assertThat(testJsonActual).isEqualTo(testJsonExpected)
     }
+
+    @Test
+    fun getter_frameworkProperties_success() {
+        val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val allowHybridExpected = false
+        val expectedData = Bundle()
+        expectedData.putString(
+            GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON,
+            requestJsonExpected
+        )
+        expectedData.putBoolean(
+            GetPublicKeyCredentialOption.BUNDLE_KEY_ALLOW_HYBRID,
+            allowHybridExpected
+        )
+
+        val option = GetPublicKeyCredentialOption(requestJsonExpected, allowHybridExpected)
+
+        assertThat(option.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+        assertThat(equals(option.data, expectedData)).isTrue()
+        assertThat(option.requireSystemProvider).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
new file mode 100644
index 0000000..c41d985
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PasswordCredentialJavaTest {
+    @Test
+    public void constructor_nullId_throws() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new PasswordCredential(null, "pwd")
+        );
+    }
+
+    @Test
+    public void constructor_nullPassword_throws() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new PasswordCredential("id", null)
+        );
+    }
+
+    @Test
+    public void constructor_emptyPassword_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new PasswordCredential("id", "")
+        );
+    }
+
+    @Test
+    public void getter_id() {
+        String idExpected = "id";
+        PasswordCredential credential = new PasswordCredential(idExpected, "password");
+        assertThat(credential.getId()).isEqualTo(idExpected);
+    }
+
+    @Test
+    public void getter_password() {
+        String passwordExpected = "pwd";
+        PasswordCredential credential = new PasswordCredential("id", passwordExpected);
+        assertThat(credential.getPassword()).isEqualTo(passwordExpected);
+    }
+
+    @Test
+    public void getter_frameworkProperties() {
+        String idExpected = "id";
+        String passwordExpected = "pwd";
+        Bundle expectedData = new Bundle();
+        expectedData.putString(PasswordCredential.BUNDLE_KEY_ID, idExpected);
+        expectedData.putString(PasswordCredential.BUNDLE_KEY_PASSWORD, passwordExpected);
+
+        CreatePasswordRequest credential = new CreatePasswordRequest(idExpected, passwordExpected);
+
+        assertThat(credential.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(credential.getData(), expectedData)).isTrue();
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
new file mode 100644
index 0000000..c8f425c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 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.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PasswordCredentialTest {
+    @Test
+    fun constructor_emptyPassword_throws() {
+        assertThrows<IllegalArgumentException> {
+            PasswordCredential("id", "")
+        }
+    }
+
+    @Test
+    fun getter_id() {
+        val idExpected = "id"
+        val credential = PasswordCredential(idExpected, "password")
+        assertThat(credential.id).isEqualTo(idExpected)
+    }
+
+    @Test
+    fun getter_password() {
+        val passwordExpected = "pwd"
+        val credential = PasswordCredential("id", passwordExpected)
+        assertThat(credential.password).isEqualTo(passwordExpected)
+    }
+
+    @Test
+    fun getter_frameworkProperties() {
+        val idExpected = "id"
+        val passwordExpected = "pwd"
+        val expectedData = Bundle()
+        expectedData.putString(PasswordCredential.BUNDLE_KEY_ID, idExpected)
+        expectedData.putString(PasswordCredential.BUNDLE_KEY_PASSWORD, passwordExpected)
+
+        val credential = PasswordCredential(idExpected, passwordExpected)
+
+        assertThat(credential.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+        assertThat(equals(credential.data, expectedData)).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
index 86847fa..04223aa 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
@@ -20,6 +20,8 @@
 
 import static org.junit.Assert.assertThrows;
 
+import android.os.Bundle;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -64,6 +66,20 @@
     }
 
     @Test
+    public void getter_frameworkProperties() {
+        String jsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        Bundle expectedData = new Bundle();
+        expectedData.putString(
+                PublicKeyCredential.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, jsonExpected);
+
+        PublicKeyCredential publicKeyCredential = new PublicKeyCredential(jsonExpected);
+
+        assertThat(publicKeyCredential.getType()).isEqualTo(
+                PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+        assertThat(TestUtilsKt.equals(publicKeyCredential.getData(), expectedData)).isTrue();
+    }
+
+    @Test
     public void staticProperty_hasCorrectTypeConstantValue() {
         String typeExpected = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
         String typeActual = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL;
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
index 47aecb4..0c67994 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.credentials
 
+import android.os.Bundle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -54,6 +55,22 @@
     }
 
     @Test
+    fun getter_frameworkProperties() {
+        val jsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val expectedData = Bundle()
+        expectedData.putString(
+            PublicKeyCredential.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, jsonExpected
+        )
+
+        val publicKeyCredential = PublicKeyCredential(jsonExpected)
+
+        assertThat(publicKeyCredential.type).isEqualTo(
+            PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
+        )
+        assertThat(equals(publicKeyCredential.data, expectedData)).isTrue()
+    }
+
+    @Test
     fun staticProperty_hasCorrectTypeConstantValue() {
         val typeExpected = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
         val typeActual = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
new file mode 100644
index 0000000..4567380
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 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.credentials
+
+import android.os.Bundle
+
+/** True if the two Bundles contain the same elements, and false otherwise. */
+@Suppress("DEPRECATION")
+fun equals(a: Bundle, b: Bundle): Boolean {
+    if (a.keySet().size != b.keySet().size) {
+        return false
+    }
+    for (key in a.keySet()) {
+        if (!b.keySet().contains(key)) {
+            return false
+        }
+
+        val valA = a.get(key)
+        val valB = b.get(key)
+        if (valA is Bundle && valB is Bundle && !equals(valA, valB)) {
+            return false
+        } else {
+            val isEqual = (valA?.equals(valB) ?: (valB == null))
+            if (!isEqual) {
+                return false
+            }
+        }
+    }
+    return true
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
index d630fa7..7c373fe 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
@@ -16,11 +16,22 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+
 /**
  * Base request class for registering a credential.
  *
  * An application can construct a subtype request and call [CredentialManager.executeCreateCredential] to
  * launch framework UI flows to collect consent and any other metadata needed from the user to
  * register a new user credential.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the request data in the [Bundle] format
+ * @property requireSystemProvider true if must only be fulfilled by a system provider and false
+ *                              otherwise
  */
-abstract class CreateCredentialRequest
\ No newline at end of file
+open class CreateCredentialRequest(
+    val type: String,
+    val data: Bundle,
+    val requireSystemProvider: Boolean,
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
index 192d472..5267997 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
@@ -16,5 +16,13 @@
 
 package androidx.credentials
 
-/** Base response class for registering a credential. */
-abstract class CreateCredentialResponse
\ No newline at end of file
+import android.os.Bundle
+
+/**
+ * Base response class for the credential creation operation made with the
+ * [CreateCredentialRequest].
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the response data in the [Bundle] format
+ */
+open class CreateCredentialResponse(val type: String, val data: Bundle)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
index 2fc0116..f03c633 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * A request to save the user password credential with their password provider.
  *
@@ -28,9 +31,29 @@
 class CreatePasswordRequest constructor(
     val id: String,
     val password: String,
-) : CreateCredentialRequest() {
+) : CreateCredentialRequest(
+    PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+    toBundle(id, password),
+    false,
+) {
 
     init {
         require(password.isNotEmpty()) { "password should not be empty" }
     }
+
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+
+        @JvmStatic
+        internal fun toBundle(id: String, password: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_ID, id)
+            bundle.putString(BUNDLE_KEY_PASSWORD, password)
+            return bundle
+        }
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt
index d70468c..436db69 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt
@@ -16,5 +16,10 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+
 /** A response of a password saving flow. */
-class CreatePasswordResponse : CreateCredentialResponse()
\ No newline at end of file
+class CreatePasswordResponse : CreateCredentialResponse(
+    PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+    Bundle(),
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
index e78b5ac..7fbe48a 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * Base request class for registering a public key credential.
  *
@@ -30,10 +33,19 @@
  * @hide
  */
 abstract class CreatePublicKeyCredentialBaseRequest constructor(
-    val requestJson: String
-) : CreateCredentialRequest() {
+    val requestJson: String,
+    type: String,
+    data: Bundle,
+    requireSystemProvider: Boolean,
+) : CreateCredentialRequest(type, data, requireSystemProvider) {
 
     init {
         require(requestJson.isNotEmpty()) { "request json must not be empty" }
     }
+
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+        const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
index d0b2d24..fe79aa99 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * A request to register a passkey from the user's public key credential provider.
  *
@@ -32,4 +35,23 @@
     requestJson: String,
     @get:JvmName("allowHybrid")
     val allowHybrid: Boolean = true
-) : CreatePublicKeyCredentialBaseRequest(requestJson)
\ No newline at end of file
+) : CreatePublicKeyCredentialBaseRequest(
+    requestJson,
+    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+    toBundle(requestJson, allowHybrid),
+    false,
+) {
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+        @JvmStatic
+        internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
index 23abbfb..1641f43 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * A privileged request to register a passkey from the user’s public key credential provider, where
  * the caller can modify the rp. Only callers with privileged permission, e.g. user’s default
@@ -38,7 +41,12 @@
     val clientDataHash: String,
     @get:JvmName("allowHybrid")
     val allowHybrid: Boolean = true
-) : CreatePublicKeyCredentialBaseRequest(requestJson) {
+) : CreatePublicKeyCredentialBaseRequest(
+    requestJson,
+    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+    toBundle(requestJson, rp, clientDataHash, allowHybrid),
+    false,
+) {
 
     init {
         require(rp.isNotEmpty()) { "rp must not be empty" }
@@ -88,4 +96,30 @@
                 this.rp, this.clientDataHash, this.allowHybrid)
         }
     }
+
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP"
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_CLIENT_DATA_HASH =
+            "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+        @JvmStatic
+        internal fun toBundle(
+            requestJson: String,
+            rp: String,
+            clientDataHash: String,
+            allowHybrid: Boolean
+        ): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putString(BUNDLE_KEY_RP, rp)
+            bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+    }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
index f4cf4b9..7b5cd1d 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * A response of a public key credential (passkey) flow.
  *
@@ -28,10 +31,27 @@
  */
 class CreatePublicKeyCredentialResponse(
     val registrationResponseJson: String
-) : CreateCredentialResponse() {
+) : CreateCredentialResponse(
+    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+    toBundle(registrationResponseJson)
+) {
 
     init {
         require(registrationResponseJson.isNotEmpty()) { "registrationResponseJson must not be " +
             "empty" }
     }
+
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_REGISTRATION_RESPONSE_JSON =
+            "androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON"
+
+        @JvmStatic
+        internal fun toBundle(registrationResponseJson: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REGISTRATION_RESPONSE_JSON, registrationResponseJson)
+            return bundle
+        }
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
index eb19ff8..d1d9629 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
@@ -16,5 +16,12 @@
 
 package androidx.credentials
 
-/** Base class for a credential with which the user consented to authenticate to the app. */
-abstract class Credential
\ No newline at end of file
+import android.os.Bundle
+
+/**
+ * Base class for a credential with which the user consented to authenticate to the app.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the credential data in the [Bundle] format.
+ */
+open class Credential(val type: String, val data: Bundle)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt
index dfc262c..e5c2bbf 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt
@@ -16,6 +16,8 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+
 /**
  * A federated credential fetched from a federated identity provider (FedCM).
  *
@@ -24,7 +26,10 @@
  *
  * @hide
  */
-class FederatedCredential private constructor() {
+class FederatedCredential private constructor() : Credential(
+    TYPE_FEDERATED_CREDENTIAL,
+    Bundle(),
+) {
     companion object {
         /** The type value for federated credential related operations. */
         const val TYPE_FEDERATED_CREDENTIAL: String = "type.federated_credential"
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt
index 2fcd72a..c75a157 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt
@@ -16,5 +16,21 @@
 
 package androidx.credentials
 
-/** Base request class for getting a registered credential. */
-abstract class GetCredentialOption
\ No newline at end of file
+import android.os.Bundle
+
+/**
+ * Base class for getting a specific type of credentials.
+ *
+ * [GetCredentialRequest] will be composed of a list of [GetCredentialOption] subclasses to indicate
+ * the specific credential types and configurations that your app accepts.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the request data in the [Bundle] format
+ * @property requireSystemProvider true if must only be fulfilled by a system provider and false
+ *                              otherwise
+ */
+open class GetCredentialOption(
+    val type: String,
+    val data: Bundle,
+    val requireSystemProvider: Boolean,
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
index 920cbf2..a43927e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
@@ -16,5 +16,11 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+
 /** A request to retrieve the user's saved application password from their password provider. */
-class GetPasswordOption : GetCredentialOption()
\ No newline at end of file
+class GetPasswordOption : GetCredentialOption(
+    PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+    Bundle(),
+    false,
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
index d42c089..23cd3a3 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * Base request class for getting a registered public key credential.
  *
@@ -27,10 +30,19 @@
  * @hide
  */
 abstract class GetPublicKeyCredentialBaseOption constructor(
-    val requestJson: String
-) : GetCredentialOption() {
+    val requestJson: String,
+    type: String,
+    data: Bundle,
+    requireSystemProvider: Boolean,
+) : GetCredentialOption(type, data, requireSystemProvider) {
 
     init {
         require(requestJson.isNotEmpty()) { "request json must not be empty" }
     }
+
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+        const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
index d372ffa..7f449d4 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
@@ -1,4 +1,4 @@
-package androidx.credentials/*
+/*
  * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
+package androidx.credentials
+
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * A request to get passkeys from the user's public key credential provider.
  *
@@ -30,4 +35,23 @@
     requestJson: String,
     @get:JvmName("allowHybrid")
     val allowHybrid: Boolean = true,
-) : GetPublicKeyCredentialBaseOption(requestJson)
\ No newline at end of file
+) : GetPublicKeyCredentialBaseOption(
+    requestJson,
+    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+    toBundle(requestJson, allowHybrid),
+    false
+) {
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+        @JvmStatic
+        internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
index a8fc6a2..2228e7b 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * A privileged request to get passkeys from the user's public key credential provider. The caller
  * can modify the RP. Only callers with privileged permission (e.g. user's public browser or caBLE)
@@ -38,7 +41,12 @@
     val clientDataHash: String,
     @get:JvmName("allowHybrid")
     val allowHybrid: Boolean = true
-) : GetPublicKeyCredentialBaseOption(requestJson) {
+) : GetPublicKeyCredentialBaseOption(
+    requestJson,
+    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+    toBundle(requestJson, rp, clientDataHash, allowHybrid),
+    false,
+) {
 
     init {
         require(rp.isNotEmpty()) { "rp must not be empty" }
@@ -88,4 +96,30 @@
                 this.rp, this.clientDataHash, this.allowHybrid)
         }
     }
+
+    /** @hide */
+    companion object {
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP"
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_CLIENT_DATA_HASH =
+            "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+        @JvmStatic
+        internal fun toBundle(
+            requestJson: String,
+            rp: String,
+            clientDataHash: String,
+            allowHybrid: Boolean
+        ): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putString(BUNDLE_KEY_RP, rp)
+            bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+    }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
index 252f4a1..3c42d37 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * Represents the user's password credential granted by the user for app sign-in.
  *
@@ -28,9 +31,30 @@
 class PasswordCredential constructor(
     val id: String,
     val password: String,
-) : Credential() {
+) : Credential(TYPE_PASSWORD_CREDENTIAL, toBundle(id, password)) {
 
     init {
         require(password.isNotEmpty()) { "password should not be empty" }
     }
+
+    /** @hide */
+    companion object {
+        // TODO: this type is officially defined in the framework. This definition should be
+        // removed when the framework type is available in jetpack.
+        /** @hide */
+        const val TYPE_PASSWORD_CREDENTIAL: String = "android.credentials.TYPE_PASSWORD_CREDENTIAL"
+
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+
+        @JvmStatic
+        internal fun toBundle(id: String, password: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_ID, id)
+            bundle.putString(BUNDLE_KEY_PASSWORD, password)
+            return bundle
+        }
+    }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
index ccfc29c..1a87b3c 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
@@ -16,6 +16,9 @@
 
 package androidx.credentials
 
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
 /**
  * Represents the user's passkey credential granted by the user for app sign-in.
  *
@@ -30,7 +33,10 @@
  */
 class PublicKeyCredential constructor(
     val authenticationResponseJson: String
-) : Credential() {
+) : Credential(
+    TYPE_PUBLIC_KEY_CREDENTIAL,
+    toBundle(authenticationResponseJson)
+) {
 
     init {
         require(authenticationResponseJson.isNotEmpty()) {
@@ -40,5 +46,16 @@
         /** The type value for public key credential related operations. */
         const val TYPE_PUBLIC_KEY_CREDENTIAL: String =
             "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
+
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        const val BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON =
+            "androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON"
+
+        @JvmStatic
+        internal fun toBundle(authenticationResponseJson: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, authenticationResponseJson)
+            return bundle
+        }
     }
 }
\ No newline at end of file
diff --git a/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt b/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt
index 6b969da..cbd6d75 100644
--- a/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt
+++ b/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt
@@ -43,7 +43,7 @@
      *  Marshal object to a Sink.
      *
      *  @param t the data to write to output
-     *  @output the BufferedSink to serialize data to
+     *  @param sink the BufferedSink to serialize data to
      */
     public suspend fun writeTo(t: T, sink: BufferedSink)
 }
\ No newline at end of file
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 5dcc6c6..0ad099d 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -37,16 +37,16 @@
     docs("androidx.biometric:biometric-ktx:1.2.0-alpha05")
     samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha05")
     docs("androidx.browser:browser:1.5.0-alpha01")
-    docs("androidx.camera:camera-camera2:1.2.0-rc01")
-    docs("androidx.camera:camera-core:1.2.0-rc01")
-    docs("androidx.camera:camera-extensions:1.2.0-rc01")
+    docs("androidx.camera:camera-camera2:1.3.0-alpha01")
+    docs("androidx.camera:camera-core:1.3.0-alpha01")
+    docs("androidx.camera:camera-extensions:1.3.0-alpha01")
     stubs(fileTree(dir: "../camera/camera-extensions-stub", include: ["camera-extensions-stub.jar"]))
-    docs("androidx.camera:camera-lifecycle:1.2.0-rc01")
-    docs("androidx.camera:camera-mlkit-vision:1.2.0-rc01")
+    docs("androidx.camera:camera-lifecycle:1.3.0-alpha01")
+    docs("androidx.camera:camera-mlkit-vision:1.3.0-alpha01")
     docs("androidx.camera:camera-previewview:1.1.0-beta02")
-    docs("androidx.camera:camera-video:1.2.0-rc01")
-    docs("androidx.camera:camera-view:1.2.0-rc01")
-    docs("androidx.camera:camera-viewfinder:1.2.0-rc01")
+    docs("androidx.camera:camera-video:1.3.0-alpha01")
+    docs("androidx.camera:camera-view:1.3.0-alpha01")
+    docs("androidx.camera:camera-viewfinder:1.3.0-alpha01")
     docs("androidx.car.app:app:1.3.0-beta01")
     docs("androidx.car.app:app-automotive:1.3.0-beta01")
     docs("androidx.car.app:app-projected:1.3.0-beta01")
@@ -54,55 +54,55 @@
     docs("androidx.cardview:cardview:1.0.0")
     docs("androidx.collection:collection:1.3.0-alpha02")
     docs("androidx.collection:collection-ktx:1.3.0-alpha02")
-    docs("androidx.compose.animation:animation:1.4.0-alpha01")
-    docs("androidx.compose.animation:animation-core:1.4.0-alpha01")
-    docs("androidx.compose.animation:animation-graphics:1.4.0-alpha01")
-    samples("androidx.compose.animation:animation-samples:1.4.0-alpha01")
-    samples("androidx.compose.animation:animation-core-samples:1.4.0-alpha01")
-    samples("androidx.compose.animation:animation-graphics-samples:1.4.0-alpha01")
-    docs("androidx.compose.foundation:foundation:1.4.0-alpha01")
-    docs("androidx.compose.foundation:foundation-layout:1.4.0-alpha01")
-    samples("androidx.compose.foundation:foundation-layout-samples:1.4.0-alpha01")
-    samples("androidx.compose.foundation:foundation-samples:1.4.0-alpha01")
-    docs("androidx.compose.material3:material3:1.1.0-alpha01")
-    samples("androidx.compose.material3:material3-samples:1.1.0-alpha01")
-    docs("androidx.compose.material3:material3-window-size-class:1.1.0-alpha01")
-    samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-alpha01")
-    docs("androidx.compose.material:material:1.4.0-alpha01")
-    docs("androidx.compose.material:material-icons-core:1.4.0-alpha01")
-    samples("androidx.compose.material:material-icons-core-samples:1.4.0-alpha01")
-    docs("androidx.compose.material:material-ripple:1.4.0-alpha01")
-    samples("androidx.compose.material:material-samples:1.4.0-alpha01")
-    docs("androidx.compose.runtime:runtime:1.4.0-alpha01")
-    docs("androidx.compose.runtime:runtime-livedata:1.4.0-alpha01")
-    samples("androidx.compose.runtime:runtime-livedata-samples:1.4.0-alpha01")
-    docs("androidx.compose.runtime:runtime-rxjava2:1.4.0-alpha01")
-    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.4.0-alpha01")
-    docs("androidx.compose.runtime:runtime-rxjava3:1.4.0-alpha01")
-    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.4.0-alpha01")
-    docs("androidx.compose.runtime:runtime-saveable:1.4.0-alpha01")
-    samples("androidx.compose.runtime:runtime-saveable-samples:1.4.0-alpha01")
-    samples("androidx.compose.runtime:runtime-samples:1.4.0-alpha01")
-    docs("androidx.compose.runtime:runtime-tracing:1.0.0-alpha01")
-    docs("androidx.compose.ui:ui:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-geometry:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-graphics:1.4.0-alpha01")
-    samples("androidx.compose.ui:ui-graphics-samples:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-test:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-test-junit4:1.4.0-alpha01")
-    samples("androidx.compose.ui:ui-test-samples:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-text:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-text-google-fonts:1.4.0-alpha01")
-    samples("androidx.compose.ui:ui-text-samples:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-tooling:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-tooling-data:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-tooling-preview:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-unit:1.4.0-alpha01")
-    samples("androidx.compose.ui:ui-unit-samples:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-util:1.4.0-alpha01")
-    docs("androidx.compose.ui:ui-viewbinding:1.4.0-alpha01")
-    samples("androidx.compose.ui:ui-viewbinding-samples:1.4.0-alpha01")
-    samples("androidx.compose.ui:ui-samples:1.4.0-alpha01")
+    docs("androidx.compose.animation:animation:1.4.0-alpha02")
+    docs("androidx.compose.animation:animation-core:1.4.0-alpha02")
+    docs("androidx.compose.animation:animation-graphics:1.4.0-alpha02")
+    samples("androidx.compose.animation:animation-samples:1.4.0-alpha02")
+    samples("androidx.compose.animation:animation-core-samples:1.4.0-alpha02")
+    samples("androidx.compose.animation:animation-graphics-samples:1.4.0-alpha02")
+    docs("androidx.compose.foundation:foundation:1.4.0-alpha02")
+    docs("androidx.compose.foundation:foundation-layout:1.4.0-alpha02")
+    samples("androidx.compose.foundation:foundation-layout-samples:1.4.0-alpha02")
+    samples("androidx.compose.foundation:foundation-samples:1.4.0-alpha02")
+    docs("androidx.compose.material3:material3:1.1.0-alpha02")
+    samples("androidx.compose.material3:material3-samples:1.1.0-alpha02")
+    docs("androidx.compose.material3:material3-window-size-class:1.1.0-alpha02")
+    samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-alpha02")
+    docs("androidx.compose.material:material:1.4.0-alpha02")
+    docs("androidx.compose.material:material-icons-core:1.4.0-alpha02")
+    samples("androidx.compose.material:material-icons-core-samples:1.4.0-alpha02")
+    docs("androidx.compose.material:material-ripple:1.4.0-alpha02")
+    samples("androidx.compose.material:material-samples:1.4.0-alpha02")
+    docs("androidx.compose.runtime:runtime:1.4.0-alpha02")
+    docs("androidx.compose.runtime:runtime-livedata:1.4.0-alpha02")
+    samples("androidx.compose.runtime:runtime-livedata-samples:1.4.0-alpha02")
+    docs("androidx.compose.runtime:runtime-rxjava2:1.4.0-alpha02")
+    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.4.0-alpha02")
+    docs("androidx.compose.runtime:runtime-rxjava3:1.4.0-alpha02")
+    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.4.0-alpha02")
+    docs("androidx.compose.runtime:runtime-saveable:1.4.0-alpha02")
+    samples("androidx.compose.runtime:runtime-saveable-samples:1.4.0-alpha02")
+    samples("androidx.compose.runtime:runtime-samples:1.4.0-alpha02")
+    docs("androidx.compose.runtime:runtime-tracing:1.0.0-alpha02")
+    docs("androidx.compose.ui:ui:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-geometry:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-graphics:1.4.0-alpha02")
+    samples("androidx.compose.ui:ui-graphics-samples:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-test:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-test-junit4:1.4.0-alpha02")
+    samples("androidx.compose.ui:ui-test-samples:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-text:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-text-google-fonts:1.4.0-alpha02")
+    samples("androidx.compose.ui:ui-text-samples:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-tooling:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-tooling-data:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-tooling-preview:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-unit:1.4.0-alpha02")
+    samples("androidx.compose.ui:ui-unit-samples:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-util:1.4.0-alpha02")
+    docs("androidx.compose.ui:ui-viewbinding:1.4.0-alpha02")
+    samples("androidx.compose.ui:ui-viewbinding-samples:1.4.0-alpha02")
+    samples("androidx.compose.ui:ui-samples:1.4.0-alpha02")
     docs("androidx.concurrent:concurrent-futures:1.1.0")
     docs("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     docs("androidx.contentpager:contentpager:1.0.0")
@@ -122,14 +122,15 @@
     docs("androidx.cursoradapter:cursoradapter:1.0.0")
     docs("androidx.customview:customview:1.2.0-alpha02")
     docs("androidx.customview:customview-poolingcontainer:1.0.0-rc01")
-    docs("androidx.datastore:datastore:1.0.0")
-    docs("androidx.datastore:datastore-core:1.0.0")
-    docs("androidx.datastore:datastore-preferences:1.0.0")
-    docs("androidx.datastore:datastore-preferences-core:1.0.0")
-    docs("androidx.datastore:datastore-preferences-rxjava2:1.0.0")
-    docs("androidx.datastore:datastore-preferences-rxjava3:1.0.0")
-    docs("androidx.datastore:datastore-rxjava2:1.0.0")
-    docs("androidx.datastore:datastore-rxjava3:1.0.0")
+    docs("androidx.datastore:datastore:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-core:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-core-okio:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-preferences:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-preferences-core:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-preferences-rxjava2:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-preferences-rxjava3:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-rxjava2:1.1.0-alpha01")
+    docs("androidx.datastore:datastore-rxjava3:1.1.0-alpha01")
     docs("androidx.documentfile:documentfile:1.1.0-alpha01")
     docs("androidx.draganddrop:draganddrop:1.0.0")
     docs("androidx.drawerlayout:drawerlayout:1.2.0-alpha01")
@@ -155,7 +156,7 @@
     docs("androidx.glance:glance-preview:1.0.0-alpha05")
     docs("androidx.glance:glance-wear-tiles:1.0.0-alpha05")
     docs("androidx.glance:glance-wear-tiles-preview:1.0.0-alpha05")
-    docs("androidx.graphics:graphics-core:1.0.0-alpha01")
+    docs("androidx.graphics:graphics-core:1.0.0-alpha02")
     docs("androidx.gridlayout:gridlayout:1.0.0")
     docs("androidx.health.connect:connect-client:1.0.0-alpha07")
     samples("androidx.health.connect:connect-client-samples:1.0.0-alpha07")
@@ -203,22 +204,22 @@
     docs("androidx.media2:media2-session:1.2.1")
     docs("androidx.media2:media2-widget:1.2.1")
     docs("androidx.media:media:1.6.0")
-    docs("androidx.mediarouter:mediarouter:1.3.1")
-    docs("androidx.mediarouter:mediarouter-testing:1.3.1")
+    docs("androidx.mediarouter:mediarouter:1.4.0-alpha01")
+    docs("androidx.mediarouter:mediarouter-testing:1.4.0-alpha01")
     docs("androidx.metrics:metrics-performance:1.0.0-alpha03")
-    docs("androidx.navigation:navigation-common:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-compose:2.6.0-alpha03")
-    samples("androidx.navigation:navigation-compose-samples:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-fragment:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-fragment-ktx:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-runtime:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-runtime-ktx:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-testing:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-ui:2.6.0-alpha03")
-    docs("androidx.navigation:navigation-ui-ktx:2.6.0-alpha03")
+    docs("androidx.navigation:navigation-common:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-compose:2.6.0-alpha04")
+    samples("androidx.navigation:navigation-compose-samples:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-fragment:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-fragment-ktx:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-runtime:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-runtime-ktx:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-testing:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-ui:2.6.0-alpha04")
+    docs("androidx.navigation:navigation-ui-ktx:2.6.0-alpha04")
     docs("androidx.paging:paging-common:3.2.0-alpha03")
     docs("androidx.paging:paging-common-ktx:3.2.0-alpha03")
     docs("androidx.paging:paging-compose:1.0.0-alpha17")
@@ -237,31 +238,35 @@
     docs("androidx.preference:preference:1.2.0")
     docs("androidx.preference:preference-ktx:1.2.0")
     docs("androidx.print:print:1.1.0-beta01")
-    docs("androidx.profileinstaller:profileinstaller:1.3.0-alpha01")
+    docs("androidx.privacysandbox.tools:tools:1.0.0-alpha01")
+    docs("androidx.privacysandbox.tools:tools-apigenerator:1.0.0-alpha01")
+    docs("androidx.privacysandbox.tools:tools-apipackager:1.0.0-alpha01")
+    docs("androidx.privacysandbox.tools:tools-core:1.0.0-alpha01")
+    docs("androidx.profileinstaller:profileinstaller:1.3.0-alpha02")
     docs("androidx.recommendation:recommendation:1.0.0")
     docs("androidx.recyclerview:recyclerview:1.3.0-rc01")
     docs("androidx.recyclerview:recyclerview-selection:2.0.0-alpha01")
     docs("androidx.remotecallback:remotecallback:1.0.0-alpha02")
     docs("androidx.resourceinspection:resourceinspection-annotation:1.0.1")
     docs("androidx.resourceinspection:resourceinspection-processor:1.0.1")
-    docs("androidx.room:room-common:2.5.0-beta01")
-    docs("androidx.room:room-guava:2.5.0-beta01")
-    docs("androidx.room:room-ktx:2.5.0-beta01")
-    docs("androidx.room:room-migration:2.5.0-beta01")
-    docs("androidx.room:room-paging:2.5.0-beta01")
-    docs("androidx.room:room-paging-guava:2.5.0-beta01")
-    docs("androidx.room:room-paging-rxjava2:2.5.0-beta01")
-    docs("androidx.room:room-paging-rxjava3:2.5.0-beta01")
-    docs("androidx.room:room-runtime:2.5.0-beta01")
-    docs("androidx.room:room-rxjava2:2.5.0-beta01")
-    docs("androidx.room:room-rxjava3:2.5.0-beta01")
-    docs("androidx.room:room-testing:2.5.0-beta01")
+    docs("androidx.room:room-common:2.5.0-beta02")
+    docs("androidx.room:room-guava:2.5.0-beta02")
+    docs("androidx.room:room-ktx:2.5.0-beta02")
+    docs("androidx.room:room-migration:2.5.0-beta02")
+    docs("androidx.room:room-paging:2.5.0-beta02")
+    docs("androidx.room:room-paging-guava:2.5.0-beta02")
+    docs("androidx.room:room-paging-rxjava2:2.5.0-beta02")
+    docs("androidx.room:room-paging-rxjava3:2.5.0-beta02")
+    docs("androidx.room:room-runtime:2.5.0-beta02")
+    docs("androidx.room:room-rxjava2:2.5.0-beta02")
+    docs("androidx.room:room-rxjava3:2.5.0-beta02")
+    docs("androidx.room:room-testing:2.5.0-beta02")
     docs("androidx.savedstate:savedstate:1.2.0")
     docs("androidx.savedstate:savedstate-ktx:1.2.0")
     docs("androidx.security:security-app-authenticator:1.0.0-alpha02")
     docs("androidx.security:security-app-authenticator-testing:1.0.0-alpha01")
-    docs("androidx.security:security-crypto:1.1.0-alpha03")
-    docs("androidx.security:security-crypto-ktx:1.1.0-alpha02")
+    docs("androidx.security:security-crypto:1.1.0-alpha04")
+    docs("androidx.security:security-crypto-ktx:1.1.0-alpha04")
     docs("androidx.security:security-identity-credential:1.0.0-alpha03")
     docs("androidx.sharetarget:sharetarget:1.2.0")
     docs("androidx.slice:slice-builders:1.1.0-alpha02")
@@ -269,21 +274,21 @@
     docs("androidx.slice:slice-core:1.1.0-alpha02")
     docs("androidx.slice:slice-view:1.1.0-alpha02")
     docs("androidx.slidingpanelayout:slidingpanelayout:1.2.0")
-    docs("androidx.sqlite:sqlite:2.3.0-beta01")
-    docs("androidx.sqlite:sqlite-framework:2.3.0-beta01")
-    docs("androidx.sqlite:sqlite-ktx:2.3.0-beta01")
+    docs("androidx.sqlite:sqlite:2.3.0-beta02")
+    docs("androidx.sqlite:sqlite-framework:2.3.0-beta02")
+    docs("androidx.sqlite:sqlite-ktx:2.3.0-beta02")
     docs("androidx.startup:startup-runtime:1.2.0-alpha01")
     docs("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
     docs("androidx.test.uiautomator:uiautomator:2.3.0-alpha01")
     docs("androidx.textclassifier:textclassifier:1.0.0-alpha04")
     docs("androidx.tracing:tracing:1.2.0-alpha02")
     docs("androidx.tracing:tracing-ktx:1.2.0-alpha02")
-    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha06")
-    docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha06")
+    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha07")
+    docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha07")
     docs("androidx.transition:transition:1.4.1")
     docs("androidx.transition:transition-ktx:1.4.1")
-    docs("androidx.tv:tv-foundation:1.0.0-alpha01")
-    docs("androidx.tv:tv-material:1.0.0-alpha01")
+    docs("androidx.tv:tv-foundation:1.0.0-alpha02")
+    docs("androidx.tv:tv-material:1.0.0-alpha02")
     docs("androidx.tvprovider:tvprovider:1.1.0-alpha01")
     docs("androidx.vectordrawable:vectordrawable:1.2.0-beta01")
     docs("androidx.vectordrawable:vectordrawable-animated:1.2.0-alpha01")
@@ -291,33 +296,33 @@
     docs("androidx.versionedparcelable:versionedparcelable:1.1.1")
     docs("androidx.viewpager2:viewpager2:1.1.0-beta01")
     docs("androidx.viewpager:viewpager:1.1.0-alpha01")
-    docs("androidx.wear.compose:compose-foundation:1.1.0-beta01")
-    samples("androidx.wear.compose:compose-foundation-samples:1.1.0-beta01")
-    docs("androidx.wear.compose:compose-material:1.1.0-beta01")
-    samples("androidx.wear.compose:compose-material-samples:1.1.0-beta01")
-    docs("androidx.wear.compose:compose-navigation:1.1.0-beta01")
-    samples("androidx.wear.compose:compose-navigation-samples:1.1.0-beta01")
+    docs("androidx.wear.compose:compose-foundation:1.1.0-rc01")
+    samples("androidx.wear.compose:compose-foundation-samples:1.1.0-rc01")
+    docs("androidx.wear.compose:compose-material:1.1.0-rc01")
+    samples("androidx.wear.compose:compose-material-samples:1.1.0-rc01")
+    docs("androidx.wear.compose:compose-navigation:1.1.0-rc01")
+    samples("androidx.wear.compose:compose-navigation-samples:1.1.0-rc01")
     docs("androidx.wear.tiles:tiles:1.1.0")
     docs("androidx.wear.tiles:tiles-material:1.1.0")
     docs("androidx.wear.tiles:tiles-proto:1.1.0")
     docs("androidx.wear.tiles:tiles-renderer:1.1.0")
     docs("androidx.wear.tiles:tiles-testing:1.1.0")
-    docs("androidx.wear.watchface:watchface:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-client:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-client-guava:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-complications:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-complications-data:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-complications-data-source:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.2.0-alpha03")
-    samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-complications-rendering:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-data:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-editor:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-editor-guava:1.2.0-alpha03")
-    samples("androidx.wear.watchface:watchface-editor-samples:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-guava:1.2.0-alpha03")
-    samples("androidx.wear.watchface:watchface-samples:1.2.0-alpha03")
-    docs("androidx.wear.watchface:watchface-style:1.2.0-alpha03")
+    docs("androidx.wear.watchface:watchface:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-client:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-client-guava:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-complications:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-complications-data:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-complications-data-source:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.2.0-alpha04")
+    samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-complications-rendering:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-data:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-editor:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-editor-guava:1.2.0-alpha04")
+    samples("androidx.wear.watchface:watchface-editor-samples:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-guava:1.2.0-alpha04")
+    samples("androidx.wear.watchface:watchface-samples:1.2.0-alpha04")
+    docs("androidx.wear.watchface:watchface-style:1.2.0-alpha04")
     docs("androidx.wear:wear:1.3.0-alpha03")
     stubs(fileTree(dir: "../wear/wear_stubs/", include: ["com.google.android.wearable-stubs.jar"]))
     docs("androidx.wear:wear-ongoing:1.1.0-alpha01")
@@ -326,19 +331,20 @@
     docs("androidx.wear:wear-input:1.2.0-alpha02")
     samples("androidx.wear:wear-input-samples:1.2.0-alpha01")
     docs("androidx.wear:wear-input-testing:1.2.0-alpha02")
-    docs("androidx.webkit:webkit:1.6.0-alpha02")
-    docs("androidx.window:window:1.1.0-alpha03")
+    docs("androidx.webkit:webkit:1.6.0-alpha03")
+    docs("androidx.window:window:1.1.0-alpha04")
     stubs(fileTree(dir: "../window/stubs/", include: ["window-sidecar-release-0.1.0-alpha01.aar"]))
+    docs("androidx.window:window-core:1.1.0-alpha04")
     stubs("androidx.window:window-extensions:1.0.0-alpha01")
-    docs("androidx.window:window-java:1.1.0-alpha03")
-    docs("androidx.window:window-rxjava2:1.1.0-alpha03")
-    docs("androidx.window:window-rxjava3:1.1.0-alpha03")
-    docs("androidx.window:window-testing:1.1.0-alpha03")
-    docs("androidx.work:work-gcm:2.8.0-beta01")
-    docs("androidx.work:work-multiprocess:2.8.0-beta01")
-    docs("androidx.work:work-runtime:2.8.0-beta01")
-    docs("androidx.work:work-runtime-ktx:2.8.0-beta01")
-    docs("androidx.work:work-rxjava2:2.8.0-beta01")
-    docs("androidx.work:work-rxjava3:2.8.0-beta01")
-    docs("androidx.work:work-testing:2.8.0-beta01")
+    docs("androidx.window:window-java:1.1.0-alpha04")
+    docs("androidx.window:window-rxjava2:1.1.0-alpha04")
+    docs("androidx.window:window-rxjava3:1.1.0-alpha04")
+    docs("androidx.window:window-testing:1.1.0-alpha04")
+    docs("androidx.work:work-gcm:2.8.0-beta02")
+    docs("androidx.work:work-multiprocess:2.8.0-beta02")
+    docs("androidx.work:work-runtime:2.8.0-beta02")
+    docs("androidx.work:work-runtime-ktx:2.8.0-beta02")
+    docs("androidx.work:work-rxjava2:2.8.0-beta02")
+    docs("androidx.work:work-rxjava3:2.8.0-beta02")
+    docs("androidx.work:work-testing:2.8.0-beta02")
 }
diff --git a/emoji2/emoji2-emojipicker/build.gradle b/emoji2/emoji2-emojipicker/build.gradle
index 4c98c732..dd40d3a 100644
--- a/emoji2/emoji2-emojipicker/build.gradle
+++ b/emoji2/emoji2-emojipicker/build.gradle
@@ -24,6 +24,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesCore)
     implementation("androidx.core:core-ktx:1.8.0")
     implementation project(path: ':emoji2:emoji2')
     implementation project(path: ':core:core')
diff --git a/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/BundledEmojiListLoaderTest.kt b/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/BundledEmojiListLoaderTest.kt
index cc5b603..f9e16bc 100644
--- a/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/BundledEmojiListLoaderTest.kt
+++ b/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/BundledEmojiListLoaderTest.kt
@@ -19,8 +19,8 @@
 import android.content.Context
 import androidx.emoji2.emojipicker.utils.FileCache
 import androidx.test.core.app.ApplicationProvider
-import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Test
@@ -28,17 +28,17 @@
 @SmallTest
 class BundledEmojiListLoaderTest {
     private val context = ApplicationProvider.getApplicationContext<Context>()
-    private val emojiCompatMetadata = EmojiPickerView.EmojiCompatMetadata(null, false)
 
     @Test
-    fun testGetCategorizedEmojiData_loaded_writeToCache() {
+    fun testGetCategorizedEmojiData_loaded_writeToCache() = runBlocking {
         // delete cache dir first
         val fileCache = FileCache.getInstance(context)
         fileCache.emojiPickerCacheDir.deleteRecursively()
         assertFalse(fileCache.emojiPickerCacheDir.exists())
 
-        BundledEmojiListLoader.load(context, emojiCompatMetadata)
-        assertTrue(BundledEmojiListLoader.categorizedEmojiData.isNotEmpty())
+        BundledEmojiListLoader.load(context)
+        val result = BundledEmojiListLoader.getCategorizedEmojiData()
+        assertTrue(result.isNotEmpty())
 
         // emoji_picker/osVersion|appVersion/ folder should be created
         val propertyFolder = fileCache.emojiPickerCacheDir.listFiles()!![0]
@@ -46,17 +46,15 @@
 
         // Number of cache files should match the size of categorizedEmojiData
         val cacheFiles = propertyFolder.listFiles()
-        assertTrue(
-            cacheFiles!!.size == BundledEmojiListLoader.categorizedEmojiData.size
-        )
+        assertTrue(cacheFiles!!.size == result.size)
     }
 
     @Test
-    fun testGetCategorizedEmojiData_loaded_readFromCache() {
+    fun testGetCategorizedEmojiData_loaded_readFromCache() = runBlocking {
         // delete cache and load again
         val fileCache = FileCache.getInstance(context)
         fileCache.emojiPickerCacheDir.deleteRecursively()
-        BundledEmojiListLoader.load(context, emojiCompatMetadata)
+        BundledEmojiListLoader.load(context)
 
         val cacheFileName = fileCache.emojiPickerCacheDir.listFiles()!![0].listFiles()!![0].name
         val emptyDefaultValue = listOf<BundledEmojiListLoader.EmojiData>()
@@ -71,21 +69,15 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 21)
-    fun testGetEmojiVariantsLookup_loaded() {
+    fun testGetEmojiVariantsLookup_loaded() = runBlocking {
         // delete cache and load again
         FileCache.getInstance(context).emojiPickerCacheDir.deleteRecursively()
-        BundledEmojiListLoader.load(context, emojiCompatMetadata)
+        BundledEmojiListLoader.load(context)
+        val result = BundledEmojiListLoader.getEmojiVariantsLookup()
 
         // 👃 has variants (👃,👃,👃🏻,👃🏼,👃🏽,👃🏾,👃🏿)
-        assertTrue(
-            BundledEmojiListLoader
-                .emojiVariantsLookup["\uD83D\uDC43"]
-            !!.contains("\uD83D\uDC43\uD83C\uDFFD")
-        )
+        assertTrue(result["\uD83D\uDC43"]!!.contains("\uD83D\uDC43\uD83C\uDFFD"))
         // 😀 has no variant
-        assertFalse(
-            BundledEmojiListLoader.emojiVariantsLookup.containsKey("\uD83D\uDE00")
-        )
+        assertFalse(result.containsKey("\uD83D\uDE00"))
     }
 }
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/BundledEmojiListLoader.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/BundledEmojiListLoader.kt
index 8795edf..4f4d9c1 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/BundledEmojiListLoader.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/BundledEmojiListLoader.kt
@@ -17,9 +17,15 @@
 package androidx.emoji2.emojipicker
 
 import android.content.Context
+import android.content.res.TypedArray
 import androidx.core.content.res.use
 import androidx.emoji2.emojipicker.utils.FileCache
 import androidx.emoji2.emojipicker.utils.UnicodeRenderableManager
+import androidx.emoji2.text.EmojiCompat
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.coroutineScope
 
 /**
  * A data loader that loads the following objects either from file based caches or from resources.
@@ -31,79 +37,80 @@
  * emoji. This allows faster variants lookup.
  */
 internal object BundledEmojiListLoader {
-    private var _categorizedEmojiData: List<EmojiDataCategory>? = null
-    private var _emojiVariantsLookup: Map<String, List<String>>? = null
+    private var categorizedEmojiData: List<EmojiDataCategory>? = null
+    private var emojiVariantsLookup: Map<String, List<String>>? = null
 
-    internal fun load(context: Context, emojiCompatMetadata: EmojiPickerView.EmojiCompatMetadata) {
+    private var deferred: List<Deferred<EmojiDataCategory>>? = null
+
+    internal suspend fun load(context: Context) {
         val categoryNames = context.resources.getStringArray(R.array.category_names)
+        val resources = if (UnicodeRenderableManager.isEmoji12Supported())
+            R.array.emoji_by_category_raw_resources_gender_inclusive
+        else
+            R.array.emoji_by_category_raw_resources
+        val emojiFileCache = FileCache.getInstance(context)
 
-        _categorizedEmojiData = context.resources
-            .obtainTypedArray(R.array.emoji_by_category_raw_resources)
-            .use { ta ->
-                val emojiFileCache = FileCache.getInstance(context)
-                (0 until ta.length()).map {
-                    val cacheFileName = getCacheFileName(it, emojiCompatMetadata)
-                    emojiFileCache.getOrPut(cacheFileName) {
-                        loadSingleCategory(
-                            context,
-                            emojiCompatMetadata,
-                            ta.getResourceId(it, 0)
-                        )
-                    }.let { data -> EmojiDataCategory(categoryNames[it], data) }
-                }.toList()
-            }
-
-        _emojiVariantsLookup =
-            _categorizedEmojiData!!
-                .map { it.emojiDataList }
-                .flatten()
-                .filter { it.variants.isNotEmpty() }
-                .associate { it.primary to it.variants }
+        deferred = context.resources
+            .obtainTypedArray(resources)
+            .use { ta -> loadEmojiAsync(ta, categoryNames, emojiFileCache, context) }
     }
 
-    internal val categorizedEmojiData: List<EmojiDataCategory>
-        get() = _categorizedEmojiData
-            ?: throw IllegalStateException("BundledEmojiListLoader.load is not called")
+    internal suspend fun getCategorizedEmojiData() =
+        categorizedEmojiData ?: deferred?.awaitAll()?.also {
+            categorizedEmojiData = it
+        } ?: throw IllegalStateException("BundledEmojiListLoader.load is not called")
 
-    internal val emojiVariantsLookup: Map<String, List<String>>
-        get() = _emojiVariantsLookup
-            ?: throw IllegalStateException("BundledEmojiListLoader.load is not called")
+    internal suspend fun getEmojiVariantsLookup() =
+        emojiVariantsLookup ?: getCategorizedEmojiData()
+            .flatMap { it.emojiDataList }
+            .filter { it.variants.isNotEmpty() }
+            .associate { it.primary to it.variants }
+            .also { emojiVariantsLookup = it }
+
+    private suspend fun loadEmojiAsync(
+        ta: TypedArray,
+        categoryNames: Array<String>,
+        emojiFileCache: FileCache,
+        context: Context
+    ): List<Deferred<EmojiDataCategory>> = coroutineScope {
+        (0 until ta.length()).map {
+            async {
+                emojiFileCache.getOrPut(getCacheFileName(it)) {
+                    loadSingleCategory(context, ta.getResourceId(it, 0))
+                }.let { data -> EmojiDataCategory(categoryNames[it], data) }
+            }
+        }
+    }
 
     private fun loadSingleCategory(
         context: Context,
-        emojiCompatMetadata: EmojiPickerView.EmojiCompatMetadata,
         resId: Int,
     ): List<EmojiData> =
         context.resources
             .openRawResource(resId)
             .bufferedReader()
             .useLines { it.toList() }
-            .map { filterRenderableEmojis(it.split(","), emojiCompatMetadata) }
+            .map { filterRenderableEmojis(it.split(",")) }
             .filter { it.isNotEmpty() }
             .map { EmojiData(it.first(), it.drop(1)) }
 
-    private fun getCacheFileName(
-        categoryIndex: Int,
-        emojiCompatMetadata: EmojiPickerView.EmojiCompatMetadata
-    ) = StringBuilder().append("emoji.v1.")
-        .append(emojiCompatMetadata.hashCode())
-        .append(".")
-        .append(categoryIndex)
-        .append(".")
-        .append(
-            if (UnicodeRenderableManager.isEmoji12Supported(emojiCompatMetadata)) 1 else 0
-        ).toString()
+    private fun getCacheFileName(categoryIndex: Int) =
+        StringBuilder().append("emoji.v1.")
+            .append(if (EmojiCompat.isConfigured()) 1 else 0)
+            .append(".")
+            .append(categoryIndex)
+            .append(".")
+            .append(if (UnicodeRenderableManager.isEmoji12Supported()) 1 else 0)
+            .toString()
 
     /**
      * To eliminate 'Tofu' (the fallback glyph when an emoji is not renderable), check the
      * renderability of emojis and keep only when they are renderable on the current device.
      */
-    private fun filterRenderableEmojis(
-        emojiList: List<String>,
-        emojiCompatMetadata: EmojiPickerView.EmojiCompatMetadata,
-    ) = emojiList.filter {
-        UnicodeRenderableManager.isEmojiRenderable(it, emojiCompatMetadata)
-    }.toList()
+    private fun filterRenderableEmojis(emojiList: List<String>) =
+        emojiList.filter {
+            UnicodeRenderableManager.isEmojiRenderable(it)
+        }.toList()
 
     internal data class EmojiData(val primary: String, val variants: List<String>)
 
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
index 2dfc5ff..baaac54 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
@@ -20,6 +20,10 @@
 import android.content.res.TypedArray
 import android.util.AttributeSet
 import android.widget.FrameLayout
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -60,10 +64,6 @@
     private lateinit var bodyView: RecyclerView
 
     init {
-        initialize(context, attrs)
-    }
-
-    private fun initialize(context: Context, attrs: AttributeSet?) {
         val typedArray: TypedArray =
             context.obtainStyledAttributes(attrs, R.styleable.EmojiPickerView, 0, 0)
         emojiGridRows = typedArray.getFloat(
@@ -74,6 +74,18 @@
             R.styleable.EmojiPickerView_emojiGridColumns,
             EmojiPickerConstants.DEFAULT_BODY_COLUMNS
         )
+        typedArray.recycle()
+
+        CoroutineScope(Dispatchers.IO).launch {
+            BundledEmojiListLoader.load(context)
+            withContext(Dispatchers.Main) {
+                showEmojiPickerView(context)
+            }
+        }
+    }
+
+    private suspend fun showEmojiPickerView(context: Context) {
+        BundledEmojiListLoader.getCategorizedEmojiData()
 
         // get emoji picker
         val emojiPicker = inflate(context, R.layout.emoji_picker, this)
@@ -91,13 +103,5 @@
             false
         )
         bodyView.adapter = EmojiPickerBodyAdapter(context, emojiGridColumns, emojiGridRows)
-
-        // recycle the typed array
-        typedArray.recycle()
     }
-
-    /**
-     * MetaVersion will be null if EmojiCompat is not enabled.
-     */
-    internal data class EmojiCompatMetadata(val metaVersion: Int?, val replaceAll: Boolean)
 }
\ No newline at end of file
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/UnicodeRenderableManager.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/UnicodeRenderableManager.kt
index b8bd327..1d56bc7 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/UnicodeRenderableManager.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/UnicodeRenderableManager.kt
@@ -20,7 +20,6 @@
 import android.text.TextPaint
 import androidx.annotation.VisibleForTesting
 import androidx.core.graphics.PaintCompat
-import androidx.emoji2.emojipicker.EmojiPickerView
 import androidx.emoji2.text.EmojiCompat
 
 /**
@@ -59,19 +58,15 @@
      *
      * Note: For older API version, codepoints {@code U+0xFE0F} are removed.
      */
-    internal fun isEmojiRenderable(
-        emoji: String,
-        emojiCompatMetaData: EmojiPickerView.EmojiCompatMetadata
-    ) = emojiCompatMetaData.metaVersion?.run {
-        EmojiCompat.get().getEmojiMatch(emoji, this) > 0
-    } ?: (getClosestRenderable(emoji) != null)
+    internal fun isEmojiRenderable(emoji: String) =
+        if (EmojiCompat.isConfigured() &&
+            EmojiCompat.get().loadState == EmojiCompat.LOAD_STATE_SUCCEEDED)
+            EmojiCompat.get().getEmojiMatch(emoji, Int.MAX_VALUE) > 0
+        else getClosestRenderable(emoji) != null
 
-    internal fun isEmoji12Supported(
-        emojiCompatMetaData: EmojiPickerView.EmojiCompatMetadata
-    ) =
-        // Yawning face is added in emoji 12 which is the first version starts to support gender
-        // inclusive emojis.
-        isEmojiRenderable(YAWNING_FACE_EMOJI, emojiCompatMetaData)
+    // Yawning face is added in emoji 12 which is the first version starts to support gender
+    // inclusive emojis.
+    internal fun isEmoji12Supported() = isEmojiRenderable(YAWNING_FACE_EMOJI)
 
     @VisibleForTesting
     fun getClosestRenderable(emoji: String): String? {
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml
index 204ee00..ba26a9a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"VOORWERPE"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLE"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"VLAE"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Geen emosiekone beskikbaar nie"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml
index f2d2255..8bced7b 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ነገሮች"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ምልክቶች"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ባንዲራዎች"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
index 06b974b..dbee125 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"الأشياء"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"الرموز"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"الأعلام"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml
index 0a486a1..27f9a48 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"বস্তু"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"চিহ্ন"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"পতাকা"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml
index d40c53c..494a404 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBYEKTLƏR"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SİMVOLLAR"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BAYRAQLAR"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml
index 71b377f..082c070 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml
index 66979f9..b7f1775 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"АБ\'ЕКТЫ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СІМВАЛЫ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"СЦЯГІ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml
index b93d21c..5bc64c0 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ПРЕДМЕТИ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛИ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ЗНАМЕНА"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml
index 4e1034c..ce02e6c 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"অবজেক্ট"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"প্রতীক"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ফ্ল্যাগ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml
index 7ed5285..f939f3a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"PREDMETI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nije dostupan nijedan emoji"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml
index edfa8ac..7b560c7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTES"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDERES"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml
index 9aa5ac6..297d62d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTY"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLY"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"VLAJKY"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml
index 893f68e..4302216 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"TING"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLER"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAG"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml
index 23bcc14..4765437 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTE"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLE"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGGEN"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml
index f5ebd3e..8b49435 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ΑΝΤΙΚΕΙΜΕΝΑ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ΣΥΜΒΟΛΑ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ΣΗΜΑΙΕΣ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml
index de4d6fb..ef401e8 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‎OBJECTS‎‏‎‎‏‎"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‎SYMBOLS‎‏‎‎‏‎"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎FLAGS‎‏‎‎‏‎"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎No emojis available‎‏‎‎‏‎"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml
index 3620abe..688cecb 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDERAS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No hay ningún emoji disponible"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml
index 1f75326..3047306 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDERAS"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml
index 3dca6c7..6d9cfc9 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTID"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÜMBOLID"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"LIPUD"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml
index ecd828a..86d8285 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTUAK"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"IKURRAK"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDERAK"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml
index 5d05a16..cc8c523 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"اشیاء"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"نمادها"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"پرچم‌ها"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml
index 9a4c1c3..9d90888 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ESINEET"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLIT"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"LIPUT"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml
index 82ce9a2..1d539cd 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLES"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"DRAPEAUX"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml
index 34fd6ba..1bc01ec 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLES"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"DRAPEAUX"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml
index 7cb2985..9a13966 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBXECTOS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml
index 50975a9..d0b15fe 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ઑબ્જેક્ટ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"પ્રતીકો"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ઝંડા"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml
index 9b732dd..e3a1b22 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ऑब्जेक्ट"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"सिंबल"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"झंडे"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml
index 0bc0396..a2dcb08 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nije dostupan nijedan emoji"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml
index 27f306f..57fbcd8d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"TÁRGYAK"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SZIMBÓLUMOK"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ZÁSZLÓK"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml
index f88c5f0..7842e75 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ԱՌԱՐԿԱՆԵՐ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ՆՇԱՆՆԵՐ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ԴՐՈՇՆԵՐ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml
index 75fe397..fcb4268 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEK"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOL"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BENDERA"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml
index 8449afb..86c6d24 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"HLUTIR"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"TÁKN"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FÁNAR"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml
index 7ae073b..91b2d1e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OGGETTI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDIERE"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml
index 0f72649..2114632 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"אובייקטים"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"סמלים"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"דגלים"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml
index aa5af40..d4bad63 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"アイテム"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"記号"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"旗"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml
index 550663f..fc2be1d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ობიექტები"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"სიმბოლოები"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"დროშები"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml
index c59ebb8..9ea1326 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"НЫСАНДАР"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ТАҢБАЛАР"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ЖАЛАУШАЛАР"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml
index 88bb970..180d981 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"វត្ថុ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"និមិត្តសញ្ញា"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ទង់ជាតិ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml
index c1bc2ae..8967259 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ವಸ್ತುಗಳು"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ಸಂಕೇತಗಳು"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ಫ್ಲ್ಯಾಗ್‌ಗಳು"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml
index 92a44db..556d96e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"사물"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"기호"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"깃발"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml
index c6c3658..bf96ba5 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ОБЪЕКТТЕР"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛДОР"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ЖЕЛЕКТЕР"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml
index 93f8f6a..58728bf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ວັດຖຸ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ສັນຍາລັກ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ທຸງ"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"ບໍ່ມີອີໂມຈິໃຫ້ນຳໃຊ້"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml
index 69c0624..6e243bf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTAI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLIAI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"VĖLIAVOS"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml
index eef9dca..eaae840 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"KAROGI"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml
index 3f844e5..194dbfe 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ОБЈЕКТИ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СИМБОЛИ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ЗНАМИЊА"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Нема достапни емоџија"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml
index 95f3bb7..6551915 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"വസ്‌തുക്കൾ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ചിഹ്നങ്ങൾ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"പതാകകൾ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml
index dfbe911..0903d6d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ОБЪЕКТ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ТЭМДЭГ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ТУГ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml
index f84380b..133595a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ऑब्जेक्ट"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"चिन्हे"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ध्वज"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml
index 4b21953..bbd44a1 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEK"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOL"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BENDERA"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Tiada emoji tersedia"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml
index 371a93a..22a6327 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"အရာဝတ္ထုများ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"သင်္ကေတများ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"အလံများ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml
index 88e1961..600a1de 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"GJENSTANDER"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLER"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGG"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml
index d60e4cb..0dfae15 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"वस्तुहरू"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"चिन्हहरू"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"झन्डाहरू"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml
index 3a4a9df..b662a97 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTEN"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLEN"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"VLAGGEN"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml
index c0a2ce0..00cdd48 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ଅବଜେକ୍ଟଗୁଡ଼ିକ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ଚିହ୍ନଗୁଡ଼ିକ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ଫ୍ଲାଗଗୁଡ଼ିକ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml
index ba141b7..0ee52cb 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ਵਸਤੂਆਂ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ਚਿੰਨ੍ਹ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ਝੰਡੇ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml
index 8720f89..427f6be 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"PRZEDMIOTY"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLE"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGI"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml
index 640b333..b8a0e5f 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Não há emojis disponíveis"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml
index 59b7ead..c6a2bf9 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nenhum emoji disponível"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml
index 640b333..b8a0e5f 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Não há emojis disponíveis"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml
index 3bbfab6..44e6db0 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBIECTE"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLURI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"STEAGURI"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml
index 2dec46a..fec27c9 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ОБЪЕКТЫ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛЫ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ФЛАГИ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml
index 9716539..cbd141a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"වස්තු"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"සංකේත"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ධජ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
index 745a0a0..93a8089 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"PREDMETY"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLY"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"VLAJKY"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nie sú k dispozícii žiadne emodži"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml
index 3e9a4bd..5f8cd6a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"PREDMETI"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml
index 116b1e4..91bcd77 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTE"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLE"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAMUJ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml
index dac0c3e..f088321 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ОБЈЕКТИ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СИМБОЛИ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ЗАСТАВЕ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml
index 57e3a2a..69b54ae 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"FÖREMÅL"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLER"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"FLAGGOR"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml
index ae733c2..9897f9b 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"VITU"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"ISHARA"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BENDERA"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml
index b250ef6..f1959f6 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"பொருட்கள்"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"சின்னங்கள்"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"கொடிகள்"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml
index 7af3e30..194ae55 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ఆబ్జెక్ట్‌లు"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"గుర్తులు"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ఫ్లాగ్‌లు"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml
index 4a5f1b7..f6f3782 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"วัตถุ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"สัญลักษณ์"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ธง"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"ไม่มีอีโมจิ"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml
index 41f0d3b..b0b0961 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"MGA BAGAY"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"MGA SIMBOLO"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"MGA BANDILA"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml
index 1ed37d1c..a6cddbf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"NESNELER"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"SEMBOLLER"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BAYRAKLAR"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml
index ec58aa6..bb41434 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ОБ’ЄКТИ"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛИ"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"ПРАПОРИ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml
index d11be3d..5ba39c4 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"آبجیکٹس"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"علامات"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"جھنڈے"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml
index a51d22f..5107f5e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml
@@ -26,4 +26,5 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"OBYEKTLAR"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"BELGILAR"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"BAYROQCHALAR"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Hech qanday emoji mavjud emas"</string>
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml
index 4a3f34a..4583227 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"ĐỒ VẬT"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"BIỂU TƯỢNG"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"CỜ"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml
index d1438f6..509d61e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"物体"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"符号"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"旗帜"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
index 32b5850..ff47ce4 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"物件"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"符號"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"旗幟"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml
index afd04e4..135da04 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"物品"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"符號"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"旗幟"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml
index fd576f9..332f6e4 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml
@@ -26,4 +26,6 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"IZINTO"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"AMASIMBULI"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"AMAFULEGI"</string>
+    <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+    <skip />
 </resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values/arrays.xml b/emoji2/emoji2-emojipicker/src/main/res/values/arrays.xml
index 1715fc6..01830cf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values/arrays.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values/arrays.xml
@@ -28,6 +28,20 @@
         <item>@raw/emoji_category_flags</item>
     </array>
 
+    <!-- The drawable resources to be used as emoji category icons. Order of the icons must match
+         order of the content descriptions below. -->
+    <array name="emoji_by_category_raw_resources_gender_inclusive">
+        <item>@raw/emoji_category_emotions</item>
+        <item>@raw/emoji_category_people_gender_inclusive</item>
+        <item>@raw/emoji_category_animals_nature</item>
+        <item>@raw/emoji_category_food_drink</item>
+        <item>@raw/emoji_category_travel_places</item>
+        <item>@raw/emoji_category_activity</item>
+        <item>@raw/emoji_category_objects</item>
+        <item>@raw/emoji_category_symbols</item>
+        <item>@raw/emoji_category_flags</item>
+    </array>
+
     <integer-array name="emoji_categories_icons">
         <item>@drawable/quantum_gm_ic_access_time_filled_vd_theme_24</item>
         <item>@drawable/gm_filled_emoji_emotions_vd_theme_24</item>
@@ -41,8 +55,6 @@
         <item>@drawable/gm_filled_flag_vd_theme_24</item>
     </integer-array>
 
-    <!-- The drawable resources to be used as emoji category icons. Order of the icons must match
-         order of the content descriptions below. -->
     <string-array name="category_names">
         <item>@string/emoji_category_emotions</item>
         <item>@string/emoji_category_people</item>
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
index 5ab086a..6ed9681 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
@@ -53,11 +53,256 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Static library support version of the framework's {@link android.app.DialogFragment}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
+ * A fragment that displays a dialog window, floating in the foreground of its
+ * activity's window.  This fragment contains a Dialog object, which it
+ * displays as appropriate based on the fragment's state.  Control of
+ * the dialog (deciding when to show, hide, dismiss it) should be done through
+ * the APIs here, not with direct calls on the dialog.
+ *
+ * <p>Implementations should override this class and implement
+ * {@link #onViewCreated(View, Bundle)} to supply the
+ * content of the dialog.  Alternatively, they can override
+ * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
+ * as an AlertDialog, with its own content.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#BasicDialog">Basic Dialog</a>
+ * <li><a href="#AlertDialog">Alert Dialog</a>
+ * <li><a href="#DialogOrEmbed">Selecting Between Dialog or Embedding</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>DialogFragment does various things to keep the fragment's lifecycle
+ * driving it, instead of the Dialog.  Note that dialogs are generally
+ * autonomous entities -- they are their own window, receiving their own
+ * input events, and often deciding on their own when to disappear (by
+ * receiving a back key event or the user clicking on a button).
+ *
+ * <p>DialogFragment needs to ensure that what is happening with the Fragment
+ * and Dialog states remains consistent.  To do this, it watches for dismiss
+ * events from the dialog and takes care of removing its own state when they
+ * happen.  This means you should use {@link #show(FragmentManager, String)},
+ * {@link #show(FragmentTransaction, String)}, or {@link #showNow(FragmentManager, String)}
+ * to add an instance of DialogFragment to your UI, as these keep track of
+ * how DialogFragment should remove itself when the dialog is dismissed.
+ *
+ * <a name="BasicDialog"></a>
+ * <h3>Basic Dialog</h3>
+ *
+ * <p>The simplest use of DialogFragment is as a floating container for the
+ * fragment's view hierarchy.  A simple implementation may look like this:
+ *
+ * <pre>{@code
+ * public class MyDialogFragment extends DialogFragment {
+ *     int mNum;
+ *
+ *     // Create a new instance of MyDialogFragment, providing "num" as an argument.
+ *     static MyDialogFragment newInstance(int num) {
+ *         MyDialogFragment f = new MyDialogFragment();
+ *
+ *         // Supply num input as an argument.
+ *         Bundle args = new Bundle();
+ *         args.putInt("num", num);
+ *         f.setArguments(args);
+ *
+ *         return f;
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState);
+ *         mNum = getArguments().getInt("num");
+ *
+ *         // Pick a style based on the num.
+ *         int style = DialogFragment.STYLE_NORMAL, theme = 0;
+ *         switch ((mNum-1)%6) {
+ *             case 1: style = DialogFragment.STYLE_NO_TITLE; break;
+ *             case 2: style = DialogFragment.STYLE_NO_FRAME; break;
+ *             case 3: style = DialogFragment.STYLE_NO_INPUT; break;
+ *             case 4: style = DialogFragment.STYLE_NORMAL; break;
+ *             case 5: style = DialogFragment.STYLE_NORMAL; break;
+ *             case 6: style = DialogFragment.STYLE_NO_TITLE; break;
+ *             case 7: style = DialogFragment.STYLE_NO_FRAME; break;
+ *             case 8: style = DialogFragment.STYLE_NORMAL; break;
+ *         }
+ *         switch ((mNum-1)%6) {
+ *             case 4: theme = android.R.style.Theme_Holo; break;
+ *             case 5: theme = android.R.style.Theme_Holo_Light_Dialog; break;
+ *             case 6: theme = android.R.style.Theme_Holo_Light; break;
+ *             case 7: theme = android.R.style.Theme_Holo_Light_Panel; break;
+ *             case 8: theme = android.R.style.Theme_Holo_Light; break;
+ *         }
+ *         setStyle(style, theme);
+ *     }
+ *
+ *     {@literal @}Override
+ *     public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ *                              Bundle savedInstanceState) {
+ *         return inflater.inflate(R.layout.fragment_dialog, container, false);
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ *         super.onViewCreated(view, savedInstanceState);
+ *
+ *         // set DialogFragment title
+ *         getDialog().setTitle("Dialog #" + mNum);
+ *     }
+ * }
+ * }</pre>
+ *
+ * <p>An example showDialog() method on the Activity could be:
+ *
+ * <pre>{@code
+ * public void showDialog() {
+ *     mStackLevel++;
+ *
+ *     // DialogFragment.show() will take care of adding the fragment
+ *     // in a transaction.  We also want to remove any currently showing
+ *     // dialog, so make our own transaction and take care of that here.
+ *     FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ *     Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
+ *     if (prev != null) {
+ *         ft.remove(prev);
+ *     }
+ *     ft.addToBackStack(null);
+ *
+ *     // Create and show the dialog.
+ *     DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
+ *     newFragment.show(ft, "dialog");
+ * }
+ * }</pre>
+ *
+ * <p>This removes any currently shown dialog, creates a new DialogFragment
+ * with an argument, and shows it as a new state on the back stack.  When the
+ * transaction is popped, the current DialogFragment and its Dialog will be
+ * destroyed, and the previous one (if any) re-shown.  Note that in this case
+ * DialogFragment will take care of popping the transaction of the Dialog that
+ * is dismissed separately from it.
+ *
+ * <a name="AlertDialog"></a>
+ * <h3>Alert Dialog</h3>
+ *
+ * <p>Instead of (or in addition to) implementing {@link #onViewCreated(View, Bundle)} to
+ * generate the view hierarchy inside of a dialog, you may implement
+ * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object.
+ *
+ * <p>This is most useful for creating an AlertDialog, allowing you
+ * to display standard alerts to the user that are managed by a fragment.
+ * A simple example implementation of this is:
+ *
+ * <pre>{@code
+ * public static class MyAlertDialogFragment extends DialogFragment {
+ *
+ *     public static MyAlertDialogFragment newInstance(int title) {
+ *         MyAlertDialogFragment frag = new MyAlertDialogFragment();
+ *         Bundle args = new Bundle();
+ *         args.putInt("title", title);
+ *         frag.setArguments(args);
+ *         return frag;
+ *     }
+ *
+ *     {@literal @}Override
+ *     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ *
+ *         return new AlertDialog.Builder(getActivity())
+ *                 .setIcon(R.drawable.alert_dialog_icon)
+ *                 .setTitle(title)
+ *                 .setPositiveButton(R.string.alert_dialog_ok,
+ *                         (dialogInterface, i) -> ((MainActivity)getActivity()).doPositiveClick())
+ *                 .setNegativeButton(R.string.alert_dialog_cancel,
+ *                         (dialogInterface, i) -> ((MainActivity)getActivity()).doNegativeClick())
+ *                 .create();
+ *         return super.onCreateDialog(savedInstanceState);
+ *     }
+ * }
+ * }</pre>
+ *
+ * <p>The activity creating this fragment may have the following methods to
+ * show the dialog and receive results from it:
+ *
+ * <pre>{@code
+ * void showDialog() {
+ *     DialogFragment newFragment = MyAlertDialogFragment.newInstance(
+ *             R.string.alert_dialog_two_buttons_title);
+ *     newFragment.show(getSupportFragmentManager(), "dialog");
+ * }
+ *
+ * public void doPositiveClick() {
+ *     // Do stuff here.
+ *     Log.i("MainActivity", "Positive click!");
+ * }
+ *
+ * public void doNegativeClick() {
+ *     // Do stuff here.
+ *     Log.i("MainActivity", "Negative click!");
+ * }
+ * }</pre>
+ *
+ * <p>Note that in this case the fragment is not placed on the back stack, it
+ * is just added as an indefinitely running fragment.  Because dialogs normally
+ * are modal, this will still operate as a back stack, since the dialog will
+ * capture user input until it is dismissed.  When it is dismissed, DialogFragment
+ * will take care of removing itself from its fragment manager.
+ *
+ * <a name="DialogOrEmbed"></a>
+ * <h3>Selecting Between Dialog or Embedding</h3>
+ *
+ * <p>A DialogFragment can still optionally be used as a normal fragment, if
+ * desired.  This is useful if you have a fragment that in some cases should
+ * be shown as a dialog and others embedded in a larger UI.  This behavior
+ * will normally be automatically selected for you based on how you are using
+ * the fragment, but can be customized with {@link #setShowsDialog(boolean)}.
+ *
+ * <p>For example, here is a simple dialog fragment:
+ *
+ * <pre>{@code
+ * public static class MyDialogFragment extends DialogFragment {
+ *     static MyDialogFragment newInstance() {
+ *         return new MyDialogFragment();
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState);
+ *
+ *         // this fragment will be displayed in a dialog
+ *         setShowsDialog(true);
+ *     }
+ *
+ *     {@literal @}Override
+ *     public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ *             Bundle savedInstanceState) {
+ *         View v = inflater.inflate(R.layout.hello_world, container, false);
+ *         View tv = v.findViewById(R.id.text);
+ *         ((TextView)tv).setText("This is an instance of MyDialogFragment");
+ *         return v;
+ *     }
+ * }
+ * }</pre>
+ *
+ * <p>An instance of this fragment can be created and shown as a dialog:
+ *
+ * <pre>{@code
+ * void showDialog() {
+ *     // Create the fragment and show it as a dialog.
+ *     DialogFragment newFragment = MyDialogFragment.newInstance();
+ *     newFragment.show(getSupportFragmentManager(), "dialog");
+ * }
+ * }</pre>
+ *
+ * <p>It can also be added as content in a view hierarchy:
+ *
+ * <pre>{@code
+ * FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ * DialogFragment newFragment = MyDialogFragment.newInstance();
+ * ft.add(R.id.embedded, newFragment);
+ * ft.commit();
+ * }</pre>
  */
 public class DialogFragment extends Fragment
         implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
diff --git a/glance/glance-appwidget/api/current.txt b/glance/glance-appwidget/api/current.txt
index 6244413..ff192ef 100644
--- a/glance/glance-appwidget/api/current.txt
+++ b/glance/glance-appwidget/api/current.txt
@@ -372,7 +372,6 @@
 package androidx.glance.appwidget.unit {
 
   public final class ColorProviderKt {
-    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
   }
 
 }
diff --git a/glance/glance-appwidget/api/public_plus_experimental_current.txt b/glance/glance-appwidget/api/public_plus_experimental_current.txt
index 27e2045..b163bae 100644
--- a/glance/glance-appwidget/api/public_plus_experimental_current.txt
+++ b/glance/glance-appwidget/api/public_plus_experimental_current.txt
@@ -386,7 +386,6 @@
 package androidx.glance.appwidget.unit {
 
   public final class ColorProviderKt {
-    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
   }
 
 }
diff --git a/glance/glance-appwidget/api/restricted_current.txt b/glance/glance-appwidget/api/restricted_current.txt
index 6244413..ff192ef 100644
--- a/glance/glance-appwidget/api/restricted_current.txt
+++ b/glance/glance-appwidget/api/restricted_current.txt
@@ -372,7 +372,6 @@
 package androidx.glance.appwidget.unit {
 
   public final class ColorProviderKt {
-    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
   }
 
 }
diff --git a/glance/glance-appwidget/integration-tests/demos/build.gradle b/glance/glance-appwidget/integration-tests/demos/build.gradle
index 5a3147a..7f1553b 100644
--- a/glance/glance-appwidget/integration-tests/demos/build.gradle
+++ b/glance/glance-appwidget/integration-tests/demos/build.gradle
@@ -27,6 +27,8 @@
     implementation(libs.kotlinStdlib)
     implementation(project(":glance:glance"))
     implementation(project(":glance:glance-appwidget"))
+    implementation(project(":glance:glance-material"))
+    implementation(project(":glance:glance-material3"))
     implementation("androidx.activity:activity:1.4.0")
     implementation("androidx.activity:activity-compose:1.4.0")
     implementation("androidx.compose.material:material:1.1.0-beta02")
@@ -36,6 +38,7 @@
     implementation("androidx.compose.material:material:1.1.0-beta02")
     implementation("androidx.datastore:datastore-preferences-core:1.0.0")
     implementation("androidx.datastore:datastore-preferences:1.0.0-rc02")
+    implementation "androidx.compose.material3:material3:1.0.0"
 }
 
 android {
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
index c9cca26..8cfbfc5 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
@@ -52,7 +52,7 @@
 import androidx.glance.appwidget.appWidgetBackground
 import androidx.glance.appwidget.cornerRadius
 import androidx.glance.appwidget.state.updateAppWidgetState
-import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.color.ColorProvider
 import androidx.glance.currentState
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.Column
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt
index eb0d0c2..c469cba 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt
@@ -43,8 +43,8 @@
 import androidx.glance.appwidget.appWidgetBackground
 import androidx.glance.appwidget.cornerRadius
 import androidx.glance.appwidget.state.updateAppWidgetState
-import androidx.glance.appwidget.unit.ColorProvider
 import androidx.glance.background
+import androidx.glance.color.ColorProvider
 import androidx.glance.currentState
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.Column
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt
index 1decabe..56bb8f4 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt
@@ -16,20 +16,32 @@
 
 package androidx.glance.appwidget.demos
 
+import android.content.Context
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.glance.Button
+import androidx.glance.GlanceId
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceTheme
+import androidx.glance.action.ActionParameters
 import androidx.glance.appwidget.CheckBox
 import androidx.glance.appwidget.GlanceAppWidget
 import androidx.glance.appwidget.GlanceAppWidgetReceiver
 import androidx.glance.appwidget.RadioButton
 import androidx.glance.appwidget.Switch
+import androidx.glance.appwidget.action.ActionCallback
+import androidx.glance.appwidget.action.actionRunCallback
 import androidx.glance.background
 import androidx.glance.layout.Column
 import androidx.glance.layout.Row
 import androidx.glance.layout.padding
+import androidx.glance.material.ColorProviders
+import androidx.glance.material3.ColorProviders
 import androidx.glance.text.Text
 import androidx.glance.text.TextStyle
 import androidx.glance.unit.ColorProvider
@@ -38,16 +50,34 @@
  * A demo showing how to construct a widget with [GlanceTheme]. It will use Material 3 colors and
  * when supported, it will use the dynamic color theme.
  */
-class DefaultColorsAppWidget : GlanceAppWidget() {
+class DefaultColorsAppWidget(private val theme: DemoColorScheme.Scheme) : GlanceAppWidget() {
 
     @Composable
     override fun Content() {
-        GlanceTheme {
+        val colors = when (theme) {
+            DemoColorScheme.Scheme.SystemM3 -> GlanceTheme.colors
+            DemoColorScheme.Scheme.CustomM3 -> ColorProviders(
+                light = DemoColorScheme.LightColors,
+                dark = DemoColorScheme.DarkColors
+            )
+
+            DemoColorScheme.Scheme.CustomM2 -> ColorProviders(
+                light = DemoColorScheme.SampleM2ColorsLight,
+                dark = DemoColorScheme.SampleM2ColorsDark
+            )
+        }
+
+        GlanceTheme(colors) {
             Column(
                 GlanceModifier
                     .padding(8.dp)
                     .background(GlanceTheme.colors.background)
             ) {
+                Button(
+                    text = "Theme: $currentScheme",
+                    >
+                    modifier = GlanceModifier.padding(2.dp)
+                )
                 Row(GlanceModifier.padding(top = 8.dp)) {
                     CheckBox(checked = false,  text = "Unchecked")
                     CheckBox(checked = true,  text = "Checked")
@@ -122,6 +152,183 @@
 
 private val doNothingAction = null
 
+class ChangeThemeCallback : ActionCallback {
+    override suspend fun onAction(
+        context: Context,
+        glanceId: GlanceId,
+        parameters: ActionParameters
+    ) {
+        colorSchemeIndex = (colorSchemeIndex + 1) % DemoColorScheme.Scheme.values().size
+        DefaultColorsAppWidget(currentScheme).update(context, glanceId)
+    }
+}
+
+private var colorSchemeIndex = 0
+private val currentScheme: DemoColorScheme.Scheme
+    get() = DemoColorScheme.Scheme.values()[colorSchemeIndex]
+
 class DefaultColorsAppWidgetReceiver : GlanceAppWidgetReceiver() {
-    override val glanceAppWidget = DefaultColorsAppWidget()
+    override val glanceAppWidget = DefaultColorsAppWidget(currentScheme)
+}
+
+/**
+ * Color scheme generated using https://m3.material.io/theme-builder#/custom
+ */
+object DemoColorScheme {
+    enum class Scheme { SystemM3, CustomM3, CustomM2 }
+
+    val md_theme_light_primary = Color(0xFF026E00)
+    val md_theme_light_onPrimary = Color(0xFFFFFFFF)
+    val md_theme_light_primaryContainer = Color(0xFF77FF61)
+    val md_theme_light_onPrimaryContainer = Color(0xFF002200)
+    val md_theme_light_secondary = Color(0xFFA900A9)
+    val md_theme_light_onSecondary = Color(0xFFFFFFFF)
+    val md_theme_light_secondaryContainer = Color(0xFFFFD7F5)
+    val md_theme_light_onSecondaryContainer = Color(0xFF380038)
+    val md_theme_light_tertiary = Color(0xFF006A6A)
+    val md_theme_light_onTertiary = Color(0xFFFFFFFF)
+    val md_theme_light_tertiaryContainer = Color(0xFF00FBFB)
+    val md_theme_light_onTertiaryContainer = Color(0xFF002020)
+    val md_theme_light_error = Color(0xFFBA1A1A)
+    val md_theme_light_errorContainer = Color(0xFFFFDAD6)
+    val md_theme_light_onError = Color(0xFFFFFFFF)
+    val md_theme_light_onErrorContainer = Color(0xFF410002)
+    val md_theme_light_background = Color(0xFFFFFBFF)
+    val md_theme_light_onBackground = Color(0xFF1E1C00)
+    val md_theme_light_surface = Color(0xFFFFFBFF)
+    val md_theme_light_onSurface = Color(0xFF1E1C00)
+    val md_theme_light_surfaceVariant = Color(0xFFDFE4D7)
+    val md_theme_light_onSurfaceVariant = Color(0xFF43483F)
+    val md_theme_light_outline = Color(0xFF73796E)
+    val md_theme_light_inverseOnSurface = Color(0xFFFFF565)
+    val md_theme_light_inverseSurface = Color(0xFF353200)
+    val md_theme_light_inversePrimary = Color(0xFF02E600)
+    val md_theme_light_shadow = Color(0xFF000000)
+    val md_theme_light_surfaceTint = Color(0xFF026E00)
+
+    val md_theme_dark_primary = Color(0xFF02E600)
+    val md_theme_dark_onPrimary = Color(0xFF013A00)
+    val md_theme_dark_primaryContainer = Color(0xFF015300)
+    val md_theme_dark_onPrimaryContainer = Color(0xFF77FF61)
+    val md_theme_dark_secondary = Color(0xFFFFABF3)
+    val md_theme_dark_onSecondary = Color(0xFF5B005B)
+    val md_theme_dark_secondaryContainer = Color(0xFF810081)
+    val md_theme_dark_onSecondaryContainer = Color(0xFFFFD7F5)
+    val md_theme_dark_tertiary = Color(0xFF00DDDD)
+    val md_theme_dark_onTertiary = Color(0xFF003737)
+    val md_theme_dark_tertiaryContainer = Color(0xFF004F4F)
+    val md_theme_dark_onTertiaryContainer = Color(0xFF00FBFB)
+    val md_theme_dark_error = Color(0xFFFFB4AB)
+    val md_theme_dark_errorContainer = Color(0xFF93000A)
+    val md_theme_dark_onError = Color(0xFF690005)
+    val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
+    val md_theme_dark_background = Color(0xFF1E1C00)
+    val md_theme_dark_onBackground = Color(0xFFF2E720)
+    val md_theme_dark_surface = Color(0xFF1E1C00)
+    val md_theme_dark_onSurface = Color(0xFFF2E720)
+    val md_theme_dark_surfaceVariant = Color(0xFF43483F)
+    val md_theme_dark_onSurfaceVariant = Color(0xFFC3C8BC)
+    val md_theme_dark_outline = Color(0xFF8D9387)
+    val md_theme_dark_inverseOnSurface = Color(0xFF1E1C00)
+    val md_theme_dark_inverseSurface = Color(0xFFF2E720)
+    val md_theme_dark_inversePrimary = Color(0xFF026E00)
+    val md_theme_dark_shadow = Color(0xFF000000)
+    val md_theme_dark_surfaceTint = Color(0xFF02E600)
+
+    val seed = Color(0xFF00FF00)
+
+    val LightColors = lightColorScheme(
+        primary = md_theme_light_primary,
+        >
+        primaryContainer = md_theme_light_primaryContainer,
+        >
+        secondary = md_theme_light_secondary,
+        >
+        secondaryContainer = md_theme_light_secondaryContainer,
+        >
+        tertiary = md_theme_light_tertiary,
+        >
+        tertiaryContainer = md_theme_light_tertiaryContainer,
+        >
+        error = md_theme_light_error,
+        >
+        errorContainer = md_theme_light_errorContainer,
+        >
+        background = md_theme_light_background,
+        >
+        surface = md_theme_light_surface,
+        >
+        surfaceVariant = md_theme_light_surfaceVariant,
+        >
+        outline = md_theme_light_outline,
+        inverseSurface = md_theme_light_inverseSurface,
+        inverseOnSurface = md_theme_light_inverseOnSurface,
+        inversePrimary = md_theme_light_inversePrimary,
+        surfaceTint = md_theme_light_surfaceTint,
+    )
+
+    val DarkColors = darkColorScheme(
+        primary = md_theme_dark_primary,
+        >
+        primaryContainer = md_theme_dark_primaryContainer,
+        >
+        secondary = md_theme_dark_secondary,
+        >
+        secondaryContainer = md_theme_dark_secondaryContainer,
+        >
+        tertiary = md_theme_dark_tertiary,
+        >
+        tertiaryContainer = md_theme_dark_tertiaryContainer,
+        >
+        error = md_theme_dark_error,
+        >
+        errorContainer = md_theme_dark_errorContainer,
+        >
+        background = md_theme_dark_background,
+        >
+        surface = md_theme_dark_surface,
+        >
+        surfaceVariant = md_theme_dark_surfaceVariant,
+        >
+        outline = md_theme_dark_outline,
+        inverseSurface = md_theme_dark_inverseSurface,
+        inverseOnSurface = md_theme_dark_inverseOnSurface,
+        inversePrimary = md_theme_dark_inversePrimary,
+        surfaceTint = md_theme_dark_surfaceTint,
+    )
+
+    // Palette based on Jetchat
+    private val Yellow400 = Color(0xFFF6E547)
+    private val Yellow700 = Color(0xFFF3B711)
+    private val Yellow800 = Color(0xFFF29F05)
+    private val Blue200 = Color(0xFF9DA3FA)
+    private val Blue400 = Color(0xFF4860F7)
+    private val Blue500 = Color(0xFF0540F2)
+    private val Blue800 = Color(0xFF001CCF)
+    private val Red300 = Color(0xFFEA6D7E)
+    private val Red800 = Color(0xFFD00036)
+
+    val SampleM2ColorsDark = darkColors(
+        primary = Blue200,
+        primaryVariant = Blue400,
+        >
+        secondary = Yellow400,
+        >
+        >
+        >
+        error = Red300,
+        >
+    )
+    val SampleM2ColorsLight = lightColors(
+        primary = Blue500,
+        primaryVariant = Blue800,
+        >
+        secondary = Yellow700,
+        secondaryVariant = Yellow800,
+        >
+        >
+        >
+        error = Red800,
+        >
+    )
 }
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ExactAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ExactAppWidget.kt
index 2857901..d257a49 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ExactAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ExactAppWidget.kt
@@ -26,7 +26,7 @@
 import androidx.glance.appwidget.SizeMode
 import androidx.glance.appwidget.background
 import androidx.glance.appwidget.cornerRadius
-import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.color.ColorProvider
 import androidx.glance.layout.Column
 import androidx.glance.layout.fillMaxSize
 import androidx.glance.layout.padding
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt
index f15b3d8..f407767 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt
@@ -16,25 +16,21 @@
 
 package androidx.glance.appwidget.demos
 
-import android.content.Context
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
-import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.glance.Button
-import androidx.glance.GlanceId
 import androidx.glance.GlanceModifier
 import androidx.glance.Image
 import androidx.glance.ImageProvider
-import androidx.glance.action.ActionParameters
 import androidx.glance.appwidget.GlanceAppWidget
 import androidx.glance.appwidget.GlanceAppWidgetReceiver
 import androidx.glance.appwidget.SizeMode
-import androidx.glance.appwidget.action.ActionCallback
-import androidx.glance.appwidget.action.actionRunCallback
-import androidx.glance.appwidget.state.updateAppWidgetState
 import androidx.glance.background
-import androidx.glance.currentState
 import androidx.glance.layout.Column
 import androidx.glance.layout.ContentScale
 import androidx.glance.layout.Spacer
@@ -42,6 +38,7 @@
 import androidx.glance.layout.fillMaxWidth
 import androidx.glance.layout.padding
 import androidx.glance.layout.size
+import androidx.glance.session.GlanceSessionManager
 
 /**
  * Sample AppWidget that showcase the [ContentScale] options for [Image]
@@ -49,54 +46,40 @@
 class ImageAppWidget : GlanceAppWidget() {
 
     override val sizeMode: SizeMode = SizeMode.Exact
-
-    companion object {
-        internal val ImageTypeKey = stringPreferencesKey("imageType")
-    }
+    override val sessionManager = GlanceSessionManager
 
     @Composable
     override fun Content() {
-        val type = currentState(ImageTypeKey) ?: "Fit"
+        var type by remember { mutableStateOf(ContentScale.Fit) }
         Column(modifier = GlanceModifier.fillMaxSize().padding(8.dp)) {
             Button(
-                text = "Content Scale: $type",
+                text = "Content Scale: ${type.asString()}",
                 modifier = GlanceModifier.fillMaxWidth(),
-                >
+                >
+                    type = when (type) {
+                        ContentScale.Crop -> ContentScale.FillBounds
+                        ContentScale.FillBounds -> ContentScale.Fit
+                        else -> ContentScale.Crop
+                    }
+                }
             )
             Spacer(GlanceModifier.size(4.dp))
             Image(
                 provider = ImageProvider(R.drawable.compose),
-                contentDescription = "Content Scale image sample (value: $type)",
-                contentScale = type.toContentScale(),
+                contentDescription = "Content Scale image sample (value: ${type.asString()})",
+                contentScale = type,
                 modifier = GlanceModifier.fillMaxSize().background(Color.DarkGray)
             )
         }
     }
 
-    private fun String.toContentScale() = when (this) {
-        "Fit" -> ContentScale.Fit
-        "Fill Bounds" -> ContentScale.FillBounds
-        "Crop" -> ContentScale.Crop
-        else -> throw IllegalArgumentException()
-    }
-}
-
-class ChangeImageAction : ActionCallback {
-    override suspend fun onAction(
-        context: Context,
-        glanceId: GlanceId,
-        parameters: ActionParameters
-    ) {
-        updateAppWidgetState(context, glanceId) { state ->
-            val value = when (state[ImageAppWidget.ImageTypeKey]) {
-                "Crop" -> "Fill Bounds"
-                "Fill Bounds" -> "Fit"
-                else -> "Crop"
-            }
-            state[ImageAppWidget.ImageTypeKey] = value
+    private fun ContentScale.asString(): String =
+        when (this) {
+            ContentScale.Fit -> "Fit"
+            ContentScale.FillBounds -> "Fill Bounds"
+            ContentScale.Crop -> "Crop"
+            else -> "Unknown content scale"
         }
-        ImageAppWidget().update(context, glanceId)
-    }
 }
 
 class ImageAppWidgetReceiver : GlanceAppWidgetReceiver() {
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/build.gradle b/glance/glance-appwidget/integration-tests/macrobenchmark-target/build.gradle
new file mode 100644
index 0000000..c4be7d4
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+
+plugins {
+    id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+    implementation(project(":glance:glance"))
+    implementation(project(":glance:glance-appwidget"))
+}
+
+android {
+    namespace "androidx.glance.appwidget.macrobenchmark.target"
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"),
+                    'proguard-rules.pro'
+        }
+    }
+
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/proguard-rules.pro b/glance/glance-appwidget/integration-tests/macrobenchmark-target/proguard-rules.pro
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/proguard-rules.pro
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2791119
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?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">
+
+    <application
+        android:allowBackup="false"
+        android:label="glance-appwidget macrobenchmark target"
+        android:supportsRtl="true">
+        <!-- Profileable to enable macrobenchmark profiling -->
+        <profileable android:shell="true"/>
+
+        <receiver
+            android:name="androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetReceiver"
+            android:label="BasicAppWidget Receiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="androidx.glance.appwidget.action.DEBUG_UPDATE" />
+                <action android:name="android.intent.action.LOCALE_CHANGED" />
+            </intent-filter>
+            <meta-data
+                android:name="android.appwidget.provider"
+                android:resource="@xml/default_app_widget_info" />
+        </receiver>
+        <receiver
+            android:name="androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetWithSessionReceiver"
+            android:label="BasicAppWidget Receiver with sessions enabled"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="androidx.glance.appwidget.action.DEBUG_UPDATE" />
+                <action android:name="android.intent.action.LOCALE_CHANGED" />
+            </intent-filter>
+            <meta-data
+                android:name="android.appwidget.provider"
+                android:resource="@xml/default_app_widget_info" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/java/androidx/glance/appwidget/macrobenchmark/target/BasicAppWidget.kt b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/java/androidx/glance/appwidget/macrobenchmark/target/BasicAppWidget.kt
new file mode 100644
index 0000000..b12aaca
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/java/androidx/glance/appwidget/macrobenchmark/target/BasicAppWidget.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.glance.appwidget.macrobenchmark.target
+
+import androidx.compose.runtime.Composable
+import androidx.datastore.preferences.core.Preferences
+import androidx.glance.GlanceModifier
+import androidx.glance.LocalSize
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.SizeMode
+import androidx.glance.appwidget.Tracing
+import androidx.glance.currentState
+import androidx.glance.layout.Column
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.session.GlanceSessionManager
+import androidx.glance.text.Text
+import kotlin.math.roundToInt
+
+open class BasicAppWidget : GlanceAppWidget() {
+    init {
+        // Ensure tracing is enabled before starting updates.
+        Tracing.enabled.set(true)
+    }
+    override val sizeMode: SizeMode = SizeMode.Single
+
+    @Composable
+    override fun Content() {
+        val size = LocalSize.current
+        // Even though this widget does not use it, accessing state will ensure that this
+        // is recomposed every time state updates, which is useful for testing.
+        currentState<Preferences>()
+        Column(
+            modifier = GlanceModifier.fillMaxSize(),
+        ) {
+            Text(
+                " Current size: ${size.width.value.roundToInt()} dp x " +
+                    "${size.height.value.roundToInt()} dp"
+            )
+        }
+    }
+}
+
+class BasicAppWidgetWithSession : BasicAppWidget() {
+    override val sessionManager = GlanceSessionManager
+}
+
+class BasicAppWidgetReceiver : GlanceAppWidgetReceiver() {
+    override val glanceAppWidget: GlanceAppWidget = BasicAppWidget()
+}
+
+class BasicAppWidgetWithSessionReceiver : GlanceAppWidgetReceiver() {
+    override val glanceAppWidget: GlanceAppWidget = BasicAppWidgetWithSession()
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/layout/glance_default_loading_layout.xml b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/layout/glance_default_loading_layout.xml
new file mode 100644
index 0000000..509cb21
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/layout/glance_default_loading_layout.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/xml/default_app_widget_info.xml b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/xml/default_app_widget_info.xml
new file mode 100644
index 0000000..35afa69
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/xml/default_app_widget_info.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="100dp"
+    android:minHeight="100dp"
+    android:initialLayout="@layout/glance_default_loading_layout"
+    android:minResizeHeight="40dp"
+    android:minResizeWidth="40dp"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen" />
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle b/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
new file mode 100644
index 0000000..8bc801c
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+android {
+    namespace "androidx.glance.appwidget.macrobenchmark"
+}
+
+android.defaultConfig {
+    minSdkVersion 23
+}
+
+dependencies {
+    implementation 'androidx.compose.ui:ui-unit:1.2.1'
+    androidTestImplementation('androidx.benchmark:benchmark-junit4:1.1.0')
+    androidTestImplementation('androidx.benchmark:benchmark-macro-junit4:1.1.0')
+    androidTestImplementation('androidx.core:core-ktx:1.7.0')
+    androidTestImplementation(project(":glance:glance"))
+    androidTestImplementation(project(":glance:glance-appwidget"))
+    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
+    androidTestImplementation(libs.kotlinTest)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testUiautomator)
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..05a1d72
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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">
+    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
+
+    <application
+        android:supportsRtl="true">
+        <uses-library android:name="android.test.runner" />
+        <activity
+            android:name="androidx.glance.appwidget.macrobenchmark.AppWidgetHostTestActivity"
+            android:configChanges="orientation|screenLayout|screenSize"
+            android:exported="true"/>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
new file mode 100644
index 0000000..93939ba
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.glance.appwidget.macrobenchmark
+
+import android.Manifest
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.Trace
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+@SdkSuppress(minSdkVersion = 29)
+class AppWidgetHostRule(
+    private var mPortraitSize: DpSize = DpSize(200.dp, 300.dp),
+    private var mLandscapeSize: DpSize = DpSize(300.dp, 200.dp),
+    useSession: Boolean = false,
+) : TestRule {
+    private val mInstrumentation = InstrumentationRegistry.getInstrumentation()
+    private val mUiAutomation = mInstrumentation.uiAutomation
+    private val targetComponent =
+        ComponentName(
+            "androidx.glance.appwidget.macrobenchmark.target",
+            if (useSession) {
+                "androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetWithSessionReceiver"
+            } else {
+                "androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetReceiver"
+            }
+        )
+
+    private val mActivityRule: ActivityScenarioRule<AppWidgetHostTestActivity> =
+        ActivityScenarioRule(
+            Intent()
+                .setComponent(
+                    ComponentName(
+                        ApplicationProvider.getApplicationContext(),
+                        AppWidgetHostTestActivity::class.java,
+                    )
+                )
+                .putExtra(
+                    AppWidgetHostTestActivity.EXTRA_TARGET_RECEIVER,
+                    targetComponent
+                )
+        )
+
+    private val mUiDevice = UiDevice.getInstance(mInstrumentation)
+
+    // Ensure the screen starts in portrait and restore the orientation on leaving
+    private val mOrientationRule = TestRule { base, _ ->
+        object : Statement() {
+            override fun evaluate() {
+                mUiDevice.freezeRotation()
+                mUiDevice.setOrientationNatural()
+                base.evaluate()
+                mUiDevice.unfreezeRotation()
+            }
+        }
+    }
+
+    private val mInnerRules = RuleChain.outerRule(mActivityRule).around(mOrientationRule)
+
+    private var mHostStarted = false
+    private var mMaybeHostView: TestAppWidgetHostView? = null
+    private var mAppWidgetId = 0
+    private val mContext = ApplicationProvider.getApplicationContext<Context>()
+
+    override fun apply(base: Statement, description: Description) = object : Statement() {
+        override fun evaluate() {
+            mInnerRules.apply(base, description).evaluate()
+            if (mHostStarted) {
+                mUiAutomation.dropShellPermissionIdentity()
+            }
+        }
+    }
+
+    /**
+     * Start the host and bind the app widget.
+     * Measures time from binding an app widget to receiving the first RemoteViews.
+     */
+    fun startHost() {
+        mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.BIND_APPWIDGET)
+        mHostStarted = true
+
+        Trace.beginSection("appWidgetInitialUpdate")
+        mActivityRule.scenario.onActivity { activity ->
+            mMaybeHostView = activity.bindAppWidget(mPortraitSize, mLandscapeSize)
+        }
+
+        val hostView = checkNotNull(mMaybeHostView) { "Host view wasn't successfully started" }
+
+        mAppWidgetId = hostView.appWidgetId
+        hostView.waitForRemoteViews()
+        Trace.endSection()
+    }
+
+    /**
+     * Measures time from sending APPWIDGET_UPDATE broadcast to receiving RemoteViews.
+     */
+    fun updateAppWidget() {
+        val intent = Intent(GlanceAppWidgetReceiver.ACTION_DEBUG_UPDATE)
+            .setPackage("androidx.glance.appwidget.macrobenchmark.target")
+            .setComponent(
+                ComponentName(
+                    "androidx.glance.appwidget.macrobenchmark.target",
+                    "androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetReceiver"
+                )
+            )
+            .putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(mAppWidgetId))
+        val hostView = checkNotNull(mMaybeHostView) { "Host view not started" }
+        Trace.beginSection("appWidgetUpdate")
+        hostView.resetRemoteViewsLatch()
+        mContext.sendBroadcast(intent)
+        hostView.waitForRemoteViews()
+        Trace.endSection()
+    }
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
new file mode 100644
index 0000000..41098fc
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
@@ -0,0 +1,234 @@
+/*
+ * 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.glance.appwidget.macrobenchmark
+
+import android.app.Activity
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.os.LocaleList
+import android.util.DisplayMetrics
+import android.util.Log
+import android.util.SizeF
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.RemoteViews
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.max
+import androidx.compose.ui.unit.min
+import java.util.Locale
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Assert.fail
+
+@RequiresApi(26)
+class AppWidgetHostTestActivity : Activity() {
+    private var mHost: AppWidgetHost? = null
+    private val mHostViews = mutableListOf<TestAppWidgetHostView>()
+
+    companion object {
+        const val EXTRA_TARGET_RECEIVER = "targetReceiver"
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+        setContentView(android.R.layout.list_content)
+
+        mHost = TestAppWidgetHost(this, 1025).also {
+            it.appWidgetIds.forEach(it::deleteAppWidgetId)
+            it.startListening()
+        }
+    }
+
+    override fun onDestroy() {
+        try {
+            mHost?.stopListening()
+            mHost?.deleteHost()
+        } catch (ex: Throwable) {
+            Log.w("AppWidgetHostTestActivity", "Error stopping the AppWidget Host", ex)
+        }
+        mHost = null
+        super.onDestroy()
+    }
+
+    private fun rootView() =
+        this.findViewById<ViewGroup>(android.R.id.content).getChildAt(0) as ViewGroup
+
+    fun bindAppWidget(portraitSize: DpSize, landscapeSize: DpSize): TestAppWidgetHostView? {
+        val host = mHost ?: error("App widgets can only be bound while the activity is created")
+
+        val appWidgetManager = AppWidgetManager.getInstance(this)
+        val appWidgetId = host.allocateAppWidgetId()
+
+        @Suppress("DEPRECATION")
+        val componentName = intent.getParcelableExtra<ComponentName>(EXTRA_TARGET_RECEIVER)!!
+
+        val wasBound = appWidgetManager.bindAppWidgetIdIfAllowed(
+            appWidgetId,
+            componentName,
+            optionsBundleOf(listOf(portraitSize, landscapeSize))
+        )
+        if (!wasBound) {
+            fail("Failed to bind the app widget")
+            mHost?.deleteHost()
+            mHost = null
+            return null
+        }
+
+        val info = appWidgetManager.getAppWidgetInfo(appWidgetId)
+        val locale = Locale.getDefault()
+        val config = resources.configuration
+        config.setLocales(LocaleList(locale))
+        config.setLayoutDirection(locale)
+        val context = this.createConfigurationContext(config)
+
+        val hostView = host.createView(context, appWidgetId, info) as TestAppWidgetHostView
+        hostView.setPadding(0, 0, 0, 0)
+        rootView().addView(hostView)
+        hostView.setSizes(portraitSize, landscapeSize)
+        hostView.setBackgroundColor(Color.WHITE)
+        mHostViews += hostView
+        return hostView
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration) {
+        super.onConfigurationChanged(newConfig)
+        updateAllSizes(newConfig.orientation)
+        reapplyRemoteViews()
+    }
+
+    fun updateAllSizes(orientation: Int) {
+        mHostViews.forEach { it.updateSize(orientation) }
+    }
+
+    fun reapplyRemoteViews() {
+        mHostViews.forEach { it.reapplyRemoteViews() }
+    }
+}
+
+@RequiresApi(26)
+class TestAppWidgetHost(context: Context, hostId: Int) : AppWidgetHost(context, hostId) {
+    override fun onCreateView(
+        context: Context,
+        appWidgetId: Int,
+        appWidget: AppWidgetProviderInfo?
+    ): AppWidgetHostView = TestAppWidgetHostView(context)
+}
+
+@RequiresApi(26)
+class TestAppWidgetHostView(context: Context) : AppWidgetHostView(context) {
+
+    init {
+        // Prevent asynchronous inflation of the App Widget
+        setExecutor(null)
+        layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+    }
+
+    private var mLatch: CountDownLatch? = null
+    private var mRemoteViews: RemoteViews? = null
+    private var mPortraitSize: DpSize = DpSize(0.dp, 0.dp)
+    private var mLandscapeSize: DpSize = DpSize(0.dp, 0.dp)
+
+    /**
+     * Wait for the new remote views to be received. If a remote views was already received, return
+     * immediately.
+     */
+    fun waitForRemoteViews() {
+        synchronized(this) {
+            mRemoteViews?.let { return }
+            mLatch = CountDownLatch(1)
+        }
+        val result = mLatch?.await(5, TimeUnit.SECONDS)!!
+        require(result) { "Timeout before getting RemoteViews" }
+    }
+
+    override fun updateAppWidget(remoteViews: RemoteViews?) {
+        super.updateAppWidget(remoteViews)
+        synchronized(this) {
+            mRemoteViews = remoteViews
+            if (remoteViews != null) {
+                mLatch?.countDown()
+            }
+        }
+    }
+
+    /** Reset the latch used to detect the arrival of a new RemoteViews. */
+    fun resetRemoteViewsLatch() {
+        synchronized(this) {
+            mRemoteViews = null
+            mLatch = null
+        }
+    }
+
+    fun setSizes(portraitSize: DpSize, landscapeSize: DpSize) {
+        mPortraitSize = portraitSize
+        mLandscapeSize = landscapeSize
+        updateSize(resources.configuration.orientation)
+    }
+
+    fun updateSize(orientation: Int) {
+        val size = when (orientation) {
+            Configuration.ORIENTATION_LANDSCAPE -> mLandscapeSize
+            Configuration.ORIENTATION_PORTRAIT -> mPortraitSize
+            else -> error("Unknown orientation ${context.resources.configuration.orientation}")
+        }
+        val displayMetrics = resources.displayMetrics
+        val width = size.width.toPixels(displayMetrics)
+        val height = size.height.toPixels(displayMetrics)
+        layoutParams = LayoutParams(width, height, Gravity.CENTER)
+        requestLayout()
+    }
+
+    private fun Dp.toPixels(displayMetrics: DisplayMetrics) =
+        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, displayMetrics).toInt()
+
+    fun reapplyRemoteViews() {
+        mRemoteViews?.let { super.updateAppWidget(it) }
+    }
+}
+
+fun optionsBundleOf(sizes: List<DpSize>): Bundle {
+    require(sizes.isNotEmpty()) { "There must be at least one size" }
+    val (minSize, maxSize) = sizes.fold(sizes[0] to sizes[0]) { acc, s ->
+        DpSize(min(acc.first.width, s.width), min(acc.first.height, s.height)) to
+            DpSize(max(acc.second.width, s.width), max(acc.second.height, s.height))
+    }
+    return Bundle().apply {
+        putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minSize.width.value.toInt())
+        putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minSize.height.value.toInt())
+        putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxSize.width.value.toInt())
+        putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxSize.height.value.toInt())
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            val sizeList = ArrayList<SizeF>(sizes.map { SizeF(it.width.value, it.height.value) })
+            putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizeList)
+        }
+    }
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
new file mode 100644
index 0000000..578f12f
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.macrobenchmark
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.TraceSectionMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.createStartupCompilationParams
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RequiresApi(Build.VERSION_CODES.Q)
+@RunWith(Parameterized::class)
+class AppWidgetUpdateBenchmark(
+    private val startupMode: StartupMode,
+    private val compilationMode: CompilationMode,
+    useGlanceSession: Boolean,
+) {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @get:Rule
+    val appWidgetHostRule = AppWidgetHostRule(useSession = useGlanceSession)
+
+    @OptIn(ExperimentalMetricApi::class)
+    @Test
+    fun initialUpdate() = benchmarkRule.measureRepeated(
+        packageName = "androidx.glance.appwidget.macrobenchmark.target",
+        metrics = listOf(
+            TraceSectionMetric("appWidgetInitialUpdate"),
+            TraceSectionMetric("GlanceAppWidget::update"),
+        ),
+        iterations = 100,
+        compilationMode = compilationMode,
+        startupMode = startupMode,
+    ) {
+        appWidgetHostRule.startHost()
+    }
+
+    @OptIn(ExperimentalMetricApi::class)
+    @Test
+    fun appWidgetUpdate() = benchmarkRule.measureRepeated(
+        packageName = "androidx.glance.appwidget.macrobenchmark.target",
+        metrics = listOf(
+            TraceSectionMetric("appWidgetUpdate"),
+            TraceSectionMetric("GlanceAppWidget::update"),
+        ),
+        iterations = 100,
+        compilationMode = compilationMode,
+        startupMode = startupMode,
+        setupBlock = {
+            appWidgetHostRule.startHost()
+            if (startupMode == StartupMode.COLD) killProcess()
+        }
+    ) {
+        appWidgetHostRule.updateAppWidget()
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "startup={0},compilation={1},useSession={2}")
+        @JvmStatic
+        fun parameters() =
+            createStartupCompilationParams(
+                startupModes = listOf(StartupMode.COLD, StartupMode.WARM),
+                compilationModes = listOf(CompilationMode.DEFAULT)
+            ).flatMap {
+                listOf(
+                    it + true,
+                    it + false,
+                )
+            }
+    }
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml
new file mode 100644
index 0000000..aed767b
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#AAAAAA"/>
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5b41847
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest />
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
index f2aa39f..64bd87c 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
@@ -42,6 +42,8 @@
 import java.util.concurrent.TimeUnit
 import kotlin.test.assertIs
 import kotlin.test.fail
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
 import org.junit.rules.RuleChain
 import org.junit.rules.TestRule
 import org.junit.runner.Description
@@ -133,11 +135,14 @@
     /**
      * Run the [block] (usually some sort of app widget update) and wait for new RemoteViews to be
      * applied.
+     *
+     * This should not be called from the main thread, i.e. in [onHostView] or [onHostActivity].
      */
     suspend fun runAndWaitForUpdate(block: suspend () -> Unit) {
         val hostView = checkNotNull(mMaybeHostView) { "Host view wasn't successfully started" }
         hostView.resetRemoteViewsLatch()
-        block()
+        withContext(Dispatchers.Main) { block() }
+        // Do not wait on the main thread so that the UI handlers can run.
         runAndWaitForChildren {
             hostView.waitForRemoteViews()
         }
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
index 2ffe9a5..3a4ec0e 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
@@ -31,8 +31,8 @@
 import androidx.glance.LocalContext
 import androidx.glance.action.actionStartActivity
 import androidx.glance.appwidget.test.R
-import androidx.glance.appwidget.unit.ColorProvider
 import androidx.glance.background
+import androidx.glance.color.ColorProvider
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.Box
 import androidx.glance.layout.Column
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
index a3ca4e5..8e5d0a2 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
@@ -34,6 +34,8 @@
 import android.widget.RadioButton
 import android.widget.TextView
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
@@ -61,6 +63,7 @@
 import androidx.glance.appwidget.state.updateAppWidgetState
 import androidx.glance.appwidget.test.R
 import androidx.glance.background
+import androidx.glance.color.ColorProvider
 import androidx.glance.currentState
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.Box
@@ -93,7 +96,9 @@
 import java.util.concurrent.atomic.AtomicReference
 import kotlin.test.assertIs
 import kotlin.test.assertNotNull
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Rule
@@ -101,10 +106,11 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SdkSuppress(minSdkVersion = 29)
 @MediumTest
 @RunWith(Parameterized::class)
-class GlanceAppWidgetReceiverTest(useSessionManager: Boolean) {
+class GlanceAppWidgetReceiverTest(val useSessionManager: Boolean) {
     @get:Rule
     val mHostRule = AppWidgetHostRule(useSessionManager = useSessionManager)
 
@@ -553,7 +559,7 @@
     }
 
     @Test
-    fun updateAll() = runBlocking {
+    fun updateAll() = runTest {
         TestGlanceAppWidget.uiDefinition = {
             Text("text")
         }
@@ -566,7 +572,7 @@
     }
 
     @Test
-    fun updateIf() = runBlocking {
+    fun updateIf() = runTest {
         val didRun = AtomicBoolean(false)
         TestGlanceAppWidget.uiDefinition = {
             currentState<Preferences>()
@@ -797,7 +803,7 @@
                 >
                 text = "Hello Checked Switch (day: Blue/Green, night: Red/Yellow)",
                 style = TextStyle(
-                    color = androidx.glance.appwidget.unit.ColorProvider(
+                    color = ColorProvider(
                         day = Color.Black,
                         night = Color.White
                     ),
@@ -805,11 +811,11 @@
                     fontStyle = FontStyle.Normal,
                 ),
                 colors = switchColors(
-                    checkedThumbColor = androidx.glance.appwidget.unit.ColorProvider(
+                    checkedThumbColor = ColorProvider(
                         day = Color.Blue,
                         night = Color.Red
                     ),
-                    checkedTrackColor = androidx.glance.appwidget.unit.ColorProvider(
+                    checkedTrackColor = ColorProvider(
                         day = Color.Green,
                         night = Color.Yellow
                     ),
@@ -854,7 +860,35 @@
     }
 
     @Test
-    fun unsetActionCallback() {
+    fun lambdaActionCallback() = runTest {
+        if (!useSessionManager) return@runTest
+        TestGlanceAppWidget.uiDefinition = {
+            val text = remember { mutableStateOf("initial") }
+            Button(
+                text = text.value,
+                >
+                    text.value = "clicked"
+                }
+            )
+        }
+
+        mHostRule.startHost()
+        var button: View? = null
+        mHostRule.onHostView { root ->
+            val text = checkNotNull(root.findChild<TextView> { it.text.toString() == "initial" })
+            button = text.parent as View
+        }
+        mHostRule.runAndWaitForUpdate {
+            button!!.performClick()
+        }
+
+        mHostRule.onHostView { root ->
+            checkNotNull(root.findChild<TextView> { it.text.toString() == "clicked" })
+        }
+    }
+
+    @Test
+    fun unsetActionCallback() = runTest {
         TestGlanceAppWidget.uiDefinition = {
             val enabled = currentState<Preferences>()[testBoolKey] ?: true
             Text(
@@ -879,13 +913,11 @@
             assertThat(view.hasOnClickListeners()).isTrue()
         }
 
-        runBlocking {
-            updateAppWidgetState(context, AppWidgetId(mHostRule.appWidgetId)) {
-                it[testBoolKey] = false
-            }
-            mHostRule.runAndWaitForUpdate {
-                TestGlanceAppWidget.update(context, AppWidgetId(mHostRule.appWidgetId))
-            }
+        updateAppWidgetState(context, AppWidgetId(mHostRule.appWidgetId)) {
+            it[testBoolKey] = false
+        }
+        mHostRule.runAndWaitForUpdate {
+            TestGlanceAppWidget.update(context, AppWidgetId(mHostRule.appWidgetId))
         }
 
         mHostRule.onHostView { root ->
@@ -898,7 +930,7 @@
     }
 
     @Test
-    fun unsetCompoundButtonActionCallback() {
+    fun unsetCompoundButtonActionCallback() = runTest {
         TestGlanceAppWidget.uiDefinition = {
             val enabled = currentState<Preferences>()[testBoolKey] ?: true
             CheckBox(
@@ -925,13 +957,11 @@
             "checkbox" to true
         )
 
-        runBlocking {
-            updateAppWidgetState(context, AppWidgetId(mHostRule.appWidgetId)) {
-                it[testBoolKey] = false
-            }
-            mHostRule.runAndWaitForUpdate {
-                TestGlanceAppWidget.update(context, AppWidgetId(mHostRule.appWidgetId))
-            }
+        updateAppWidgetState(context, AppWidgetId(mHostRule.appWidgetId)) {
+            it[testBoolKey] = false
+        }
+        mHostRule.runAndWaitForUpdate {
+            TestGlanceAppWidget.update(context, AppWidgetId(mHostRule.appWidgetId))
         }
 
         CompoundButtonActionTest.received.set(emptyList())
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt
index 2250fbc..c3ddc3e 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt
@@ -33,6 +33,7 @@
 import androidx.glance.LocalContext
 import androidx.glance.LocalGlanceId
 import androidx.glance.LocalState
+import androidx.glance.action.LambdaAction
 import androidx.glance.session.Session
 import androidx.glance.session.SetContentFn
 import androidx.glance.state.ConfigManager
@@ -69,6 +70,7 @@
 
     private val glanceState = mutableStateOf(emptyPreferences(), neverEqualPolicy())
     private val options = mutableStateOf(Bundle(), neverEqualPolicy())
+    private var lambdas = mapOf<String, List<LambdaAction>>()
     @VisibleForTesting
     internal var lastRemoteViews: RemoteViews? = null
 
@@ -112,17 +114,23 @@
     ) {
         root as RemoteViewsRoot
         val layoutConfig = LayoutConfiguration.load(context, id.appWidgetId)
+        val appWidgetManager = context.appWidgetManager
         try {
+            val receiver = requireNotNull(appWidgetManager.getAppWidgetInfo(id.appWidgetId)) {
+                "No app widget info for ${id.appWidgetId}"
+            }.provider
             normalizeCompositionTree(root)
+            lambdas = root.updateLambdaActionKeys()
             val rv = translateComposition(
                 context,
                 id.appWidgetId,
                 root,
                 layoutConfig,
                 layoutConfig.addLayout(root),
-                DpSize.Unspecified
+                DpSize.Unspecified,
+                receiver
             )
-            context.appWidgetManager.updateAppWidget(id.appWidgetId, rv)
+            appWidgetManager.updateAppWidget(id.appWidgetId, rv)
             lastRemoteViews = rv
         } catch (ex: CancellationException) {
             // Nothing to do
@@ -132,10 +140,11 @@
             }
             logException(throwable)
             val rv = RemoteViews(context.packageName, widget.errorUiLayout)
-            context.appWidgetManager.updateAppWidget(id.appWidgetId, rv)
+            appWidgetManager.updateAppWidget(id.appWidgetId, rv)
             lastRemoteViews = rv
         } finally {
             layoutConfig.save()
+            Tracing.endGlanceAppWidgetUpdate()
         }
     }
 
@@ -156,6 +165,14 @@
                 }
                 options.value = event.newOptions
             }
+            is RunLambda -> {
+                Log.i(TAG, "Received RunLambda(${event.key}) action for session($key)")
+                lambdas[event.key]?.map { it.block() }
+                    ?: Log.w(
+                        TAG,
+                        "Triggering Action(${event.key}) for session($key) failed"
+                    )
+            }
             else -> {
                 throw IllegalArgumentException(
                     "Sent unrecognized event type ${event.javaClass} to AppWidgetSession"
@@ -172,12 +189,17 @@
         sendEvent(UpdateAppWidgetOptions(newOptions))
     }
 
+    suspend fun runLambda(key: String) {
+        sendEvent(RunLambda(key))
+    }
+
     // Action types that this session supports.
     @VisibleForTesting
     internal object UpdateGlanceState
-
     @VisibleForTesting
     internal class UpdateAppWidgetOptions(val newOptions: Bundle)
+    @VisibleForTesting
+    internal class RunLambda(val key: String)
 
     private val Context.appWidgetManager: AppWidgetManager
         get() = this.getSystemService(Context.APPWIDGET_SERVICE) as AppWidgetManager
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt
index e9063f8a..1ae6a21 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt
@@ -18,12 +18,18 @@
 
 import android.appwidget.AppWidgetManager
 import android.appwidget.AppWidgetProviderInfo
+import android.os.Build
 import android.os.Bundle
+import android.os.Trace
 import android.util.DisplayMetrics
 import android.util.Log
 import android.util.SizeF
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
+import java.util.concurrent.atomic.AtomicBoolean
 import kotlin.math.ceil
 import kotlin.math.min
 
@@ -148,4 +154,41 @@
 
 internal fun logException(throwable: Throwable) {
     Log.e(GlanceAppWidgetTag, "Error in Glance App Widget", throwable)
+}
+
+/**
+ * [Tracing] contains methods for tracing sections of GlanceAppWidget.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object Tracing {
+    val enabled = AtomicBoolean(false)
+
+    fun beginGlanceAppWidgetUpdate() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && enabled.get()) {
+            TracingApi29Impl.beginAsyncSection("GlanceAppWidget::update", 0)
+        }
+    }
+
+    fun endGlanceAppWidgetUpdate() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && enabled.get()) {
+            TracingApi29Impl.endAsyncSection("GlanceAppWidget::update", 0)
+        }
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+internal object TracingApi29Impl {
+    @DoNotInline
+    fun beginAsyncSection(
+        methodName: String,
+        cookie: Int,
+    ) = Trace.beginAsyncSection(methodName, cookie)
+
+    @DoNotInline
+    fun endAsyncSection(
+        methodName: String,
+        cookie: Int,
+    ) = Trace.endAsyncSection(methodName, cookie)
 }
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt
index 482316b..c94a102 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt
@@ -41,7 +41,7 @@
 import androidx.glance.VisibilityModifier
 import androidx.glance.action.ActionModifier
 import androidx.glance.appwidget.action.applyAction
-import androidx.glance.appwidget.unit.DayNightColorProvider
+import androidx.glance.color.DayNightColorProvider
 import androidx.glance.layout.HeightModifier
 import androidx.glance.layout.PaddingModifier
 import androidx.glance.layout.WidthModifier
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Background.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Background.kt
index e79e388..72dbe14 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Background.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Background.kt
@@ -18,8 +18,8 @@
 
 import androidx.compose.ui.graphics.Color
 import androidx.glance.GlanceModifier
-import androidx.glance.appwidget.unit.ColorProvider
 import androidx.glance.background
+import androidx.glance.color.ColorProvider
 
 /**
  * Apply a background color to the element this modifier is attached to. This will cause the
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
index 56938e9..d12e0c6 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
@@ -124,7 +124,10 @@
     /**
      * Triggers the composition of [Content] and sends the result to the [AppWidgetManager].
      */
-    suspend fun update(context: Context, glanceId: GlanceId) {
+    suspend fun update(
+        context: Context,
+        glanceId: GlanceId
+    ) {
         require(glanceId is AppWidgetId) {
             "The glanceId '$glanceId' is not a valid App Widget glance id"
         }
@@ -161,6 +164,7 @@
         appWidgetId: Int,
         options: Bundle? = null,
     ) {
+        Tracing.beginGlanceAppWidgetUpdate()
         sessionManager?.let {
             val glanceId = AppWidgetId(appWidgetId)
             if (!it.isSessionRunning(context, glanceId.toSessionKey())) {
@@ -184,6 +188,31 @@
     }
 
     /**
+     * Trigger an action to be run in the AppWidgetSession for this widget, starting the session if
+     * necessary.
+     */
+    internal suspend fun triggerAction(
+        context: Context,
+        appWidgetId: Int,
+        actionKey: String,
+        options: Bundle? = null,
+    ) {
+        sessionManager?.let { manager ->
+            val glanceId = AppWidgetId(appWidgetId)
+            val session = if (!manager.isSessionRunning(context, glanceId.toSessionKey())) {
+                AppWidgetSession(this, glanceId, options).also { session ->
+                    manager.startSession(context, session)
+                }
+            } else {
+                manager.getSession(glanceId.toSessionKey()) as AppWidgetSession
+            }
+            session.runLambda(actionKey)
+        } ?: error(
+            "GlanceAppWidget.triggerAction may only be used when a SessionManager is provided"
+        )
+    }
+
+    /**
      * Internal method called when a resize event is detected.
      */
     internal suspend fun resize(
@@ -489,6 +518,8 @@
             logException(throwable)
             val rv = RemoteViews(context.packageName, errorUiLayout)
             appWidgetManager.updateAppWidget(appWidgetId, rv)
+        } finally {
+            Tracing.endGlanceAppWidgetUpdate()
         }
     }
 
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
index 461eace..93d8962 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
@@ -25,6 +25,7 @@
 import android.os.Bundle
 import android.util.Log
 import androidx.annotation.CallSuper
+import androidx.glance.appwidget.action.LambdaActionBroadcasts
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.async
@@ -123,22 +124,35 @@
     }
 
     override fun onReceive(context: Context, intent: Intent) {
-        val forceUpdateAllWidgets = intent.action == Intent.ACTION_LOCALE_CHANGED ||
-            intent.action == ACTION_DEBUG_UPDATE
-
         runAndLogExceptions {
-            if (forceUpdateAllWidgets) {
-                val appWidgetManager = AppWidgetManager.getInstance(context)
-                val componentName =
-                    ComponentName(context.packageName, checkNotNull(javaClass.canonicalName))
-                onUpdate(
-                    context,
-                    appWidgetManager,
-                    appWidgetManager.getAppWidgetIds(componentName)
-                )
-                return
+            when (intent.action) {
+                Intent.ACTION_LOCALE_CHANGED, ACTION_DEBUG_UPDATE -> {
+                    val appWidgetManager = AppWidgetManager.getInstance(context)
+                    val componentName =
+                        ComponentName(context.packageName, checkNotNull(javaClass.canonicalName))
+                    val ids = if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)) {
+                        intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)!!
+                    } else {
+                        appWidgetManager.getAppWidgetIds(componentName)
+                    }
+                    onUpdate(
+                        context,
+                        appWidgetManager,
+                        ids,
+                    )
+                }
+                LambdaActionBroadcasts.ActionTriggerLambda -> {
+                    val actionKey = intent.getStringExtra(LambdaActionBroadcasts.ExtraActionKey)
+                            ?: error("Intent is missing ActionKey extra")
+                    val id = intent.getIntExtra(LambdaActionBroadcasts.ExtraAppWidgetId, -1)
+                    if (id == -1) error("Intent is missing AppWidgetId extra")
+                    goAsync {
+                        updateManager(context)
+                        glanceAppWidget.triggerAction(context, id, actionKey)
+                    }
+                }
+                else -> super.onReceive(context, intent)
             }
-            super.onReceive(context, intent)
         }
     }
 }
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt
index a191446..6f7a7cc 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt
@@ -26,6 +26,7 @@
 import androidx.glance.GlanceModifier
 import androidx.glance.ImageProvider
 import androidx.glance.action.ActionModifier
+import androidx.glance.action.LambdaAction
 import androidx.glance.appwidget.lazy.EmittableLazyListItem
 import androidx.glance.background
 import androidx.glance.extractModifier
@@ -117,6 +118,41 @@
     }
 }
 
+/**
+ * Walks through the Emittable tree and updates the key for all LambdaActions.
+ *
+ * This function updates the key such that the final key is equal to the original key plus a string
+ * indicating its index among its siblings. This is because sibling Composables will often have the
+ * same key due to how [androidx.compose.runtime.currentCompositeKeyHash] works. Adding the index
+ * makes sure that all of these keys are unique.
+ *
+ * Note that, because we run the same composition multiple times for different sizes in certain
+ * modes (see [ForEachSize]), action keys in one SizeBox should mirror the action keys in other
+ * SizeBoxes, so that if an action is triggered on the widget being displayed in one size, the state
+ * will be updated for the composition in all sizes. This is why there can be multiple LambdaActions
+ * for each key, even after de-duping.
+ */
+internal fun EmittableWithChildren.updateLambdaActionKeys(): Map<String, List<LambdaAction>> =
+    children.foldIndexed(
+        mutableMapOf<String, MutableList<LambdaAction>>()
+    ) { index, actions, child ->
+        val (actionMod, modifiers) = child.modifier.extractModifier<ActionModifier>()
+        if (actionMod != null && actionMod.action is LambdaAction &&
+            child !is EmittableSizeBox && child !is EmittableLazyListItem) {
+            val action = actionMod.action as LambdaAction
+            val newKey = action.key + "+$index"
+            val newAction = LambdaAction(newKey, action.block)
+            actions.getOrPut(newKey) { mutableListOf() }.add(newAction)
+            child.modifier = modifiers.then(ActionModifier(newAction))
+        }
+        if (child is EmittableWithChildren) {
+            child.updateLambdaActionKeys().forEach { (key, childActions) ->
+                actions.getOrPut(key) { mutableListOf() }.addAll(childActions)
+            }
+        }
+        actions
+    }
+
 private fun normalizeLazyListItem(view: EmittableLazyListItem) {
     if (view.children.size == 1 && view.alignment == Alignment.CenterStart) return
     val box = EmittableBox()
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt
index 9f0ec001..de2c0e4 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt
@@ -16,6 +16,7 @@
 
 package androidx.glance.appwidget
 
+import android.content.ComponentName
 import android.content.Context
 import android.os.Build
 import android.util.Log
@@ -67,6 +68,8 @@
     layoutConfiguration: LayoutConfiguration?,
     rootViewIndex: Int,
     layoutSize: DpSize,
+    actionBroadcastReceiver: ComponentName? = null,
+
 ) =
     translateComposition(
         TranslationContext(
@@ -76,6 +79,7 @@
             layoutConfiguration,
             itemPosition = -1,
             layoutSize = layoutSize,
+            actionBroadcastReceiver = actionBroadcastReceiver,
         ),
         element.children,
         rootViewIndex,
@@ -156,6 +160,7 @@
     val layoutCollectionItemId: Int = -1,
     val canUseSelectableGroup: Boolean = false,
     val actionTargetId: Int? = null,
+    val actionBroadcastReceiver: ComponentName? = null
 ) {
     fun nextViewId() = lastViewId.incrementAndGet()
 
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ActionTrampoline.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ActionTrampoline.kt
index 0905d7e..12457db 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ActionTrampoline.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ActionTrampoline.kt
@@ -59,12 +59,14 @@
     translationContext: TranslationContext,
     viewId: Int,
     type: ActionTrampolineType,
+    extraData: String = "",
 ): Uri = Uri.Builder().apply {
     scheme(ActionTrampolineScheme)
     path(type.name)
     appendQueryParameter("appWidgetId", translationContext.appWidgetId.toString())
     appendQueryParameter("viewId", viewId.toString())
     appendQueryParameter("viewSize", translationContext.layoutSize.toString())
+    appendQueryParameter("extraData", extraData)
     if (translationContext.isLazyCollectionDescendant) {
         appendQueryParameter(
             "lazyCollection",
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ApplyAction.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ApplyAction.kt
index db61be0..0b2947c 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ApplyAction.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/ApplyAction.kt
@@ -28,6 +28,7 @@
 import androidx.core.os.bundleOf
 import androidx.glance.action.Action
 import androidx.glance.action.ActionParameters
+import androidx.glance.action.LambdaAction
 import androidx.glance.action.StartActivityAction
 import androidx.glance.action.StartActivityClassAction
 import androidx.glance.action.StartActivityComponentAction
@@ -141,6 +142,29 @@
                 PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
             )
         }
+        is LambdaAction -> {
+            requireNotNull(translationContext.actionBroadcastReceiver) {
+                "In order to use LambdaAction, actionBroadcastReceiver must be provided"
+            }
+            return PendingIntent.getBroadcast(
+                translationContext.context,
+                0,
+                LambdaActionBroadcasts.createIntent(
+                    translationContext.actionBroadcastReceiver,
+                    action.key,
+                    translationContext.appWidgetId,
+                ).apply {
+                    data =
+                        createUniqueUri(
+                            translationContext,
+                            viewId,
+                            ActionTrampolineType.CALLBACK,
+                            action.key,
+                        )
+                },
+                PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
+            )
+        }
         is CompoundButtonAction -> {
             return getPendingIntentForAction(
                 action.innerAction,
@@ -206,6 +230,20 @@
             type = ActionTrampolineType.BROADCAST,
         )
     }
+    is LambdaAction -> {
+        requireNotNull(translationContext.actionBroadcastReceiver) {
+            "In order to use LambdaAction, actionBroadcastReceiver must be provided"
+        }
+        LambdaActionBroadcasts.createIntent(
+            receiver = translationContext.actionBroadcastReceiver,
+            actionKey = action.key,
+            appWidgetId = translationContext.appWidgetId,
+        ).applyTrampolineIntent(
+            translationContext,
+            viewId = viewId,
+            type = ActionTrampolineType.BROADCAST,
+        )
+    }
     is CompoundButtonAction -> {
         getFillInIntentForAction(
             action.innerAction,
@@ -313,4 +351,4 @@
             PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
         )
     }
-}
\ No newline at end of file
+}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LambdaActionBroadcasts.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LambdaActionBroadcasts.kt
new file mode 100644
index 0000000..ebc4cde
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LambdaActionBroadcasts.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 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.glance.appwidget.action
+
+import android.content.ComponentName
+import android.content.Intent
+
+/**
+ * This class contains constants that are used for broadcast intents that trigger lambda actions.
+ *
+ * When applying a lambda action, we create the intent based on the actionkey assigned to that
+ * lambda. When the action is triggered, a broadcast is sent to the GlanceAppWidgetReceiver to
+ * trigger the lambda in the corresponding session.
+ */
+internal object LambdaActionBroadcasts {
+    internal const val ActionTriggerLambda = "ACTION_TRIGGER_LAMBDA"
+    internal const val ExtraActionKey = "EXTRA_ACTION_KEY"
+    internal const val ExtraAppWidgetId = "EXTRA_APPWIDGET_ID"
+
+    internal fun createIntent(
+        receiver: ComponentName,
+        actionKey: String,
+        appWidgetId: Int,
+    ) = Intent()
+        .setComponent(receiver)
+        .setAction(ActionTriggerLambda)
+        .putExtra(ExtraActionKey, actionKey)
+        .putExtra(ExtraAppWidgetId, appWidgetId)
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CompoundButtonTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CompoundButtonTranslator.kt
index 9b167e2..0ec0c95 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CompoundButtonTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CompoundButtonTranslator.kt
@@ -26,8 +26,8 @@
 import androidx.glance.appwidget.unit.CheckedStateSet
 import androidx.glance.appwidget.unit.CheckedUncheckedColorProvider
 import androidx.glance.appwidget.unit.ResourceCheckableColorProvider
-import androidx.glance.appwidget.unit.isNightMode
 import androidx.glance.appwidget.unit.resolveCheckedColor
+import androidx.glance.color.isNightMode
 
 internal val checkableColorProviderFallbackColor = Color.Black
 
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt
index 7e9c958..52f643c 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt
@@ -44,7 +44,7 @@
 import androidx.glance.appwidget.TranslationContext
 import androidx.glance.appwidget.applyModifiers
 import androidx.glance.appwidget.insertView
-import androidx.glance.appwidget.unit.DayNightColorProvider
+import androidx.glance.color.DayNightColorProvider
 import androidx.glance.text.EmittableText
 import androidx.glance.text.FontStyle
 import androidx.glance.text.FontWeight
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/unit/ColorProvider.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/unit/ColorProvider.kt
index 0e848d0..d6599f0 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/unit/ColorProvider.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/unit/ColorProvider.kt
@@ -25,28 +25,11 @@
 import androidx.core.content.ContextCompat
 import androidx.glance.appwidget.GlanceAppWidgetTag
 import androidx.glance.appwidget.R
+import androidx.glance.color.DayNightColorProvider
 import androidx.glance.unit.ColorProvider
 import androidx.glance.unit.FixedColorProvider
 import androidx.glance.unit.ResourceColorProvider
 
-/**
- * Returns a [ColorProvider] that provides [day] when night mode is off, and [night] when night
- * mode is on.
- */
-fun ColorProvider(day: Color, night: Color): ColorProvider {
-    return DayNightColorProvider(day, night)
-}
-
-internal data class DayNightColorProvider(val day: Color, val night: Color) : ColorProvider {
-    override fun getColor(context: Context) = getColor(context.isNightMode)
-
-    fun getColor(isNightMode: Boolean) = if (isNightMode) night else day
-}
-
-internal val Context.isNightMode: Boolean
-    get() = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
-        Configuration.UI_MODE_NIGHT_YES
-
 /** Provider of different colors depending on a checked state. */
 internal sealed interface CheckableColorProvider
 
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt
index 616ba95..ec83e46 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt
@@ -25,13 +25,18 @@
 import androidx.compose.ui.unit.dp
 import androidx.glance.Emittable
 import androidx.glance.GlanceModifier
+import androidx.glance.action.ActionModifier
+import androidx.glance.action.LambdaAction
+import androidx.glance.layout.EmittableBox
 import androidx.glance.state.GlanceStateDefinition
 import androidx.glance.state.PreferencesGlanceStateDefinition
 import androidx.glance.state.ConfigManager
 import androidx.glance.text.EmittableText
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFalse
 import kotlin.test.assertIs
+import kotlin.test.assertTrue
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
@@ -128,6 +133,57 @@
         assertThat(testState.getValueCalls).containsExactly(id.toSessionKey())
     }
 
+    @Test
+    fun processEvent_runLambda() = runTest {
+        var didRunFirst = false
+        var didRunSecond = false
+        session.processEmittableTree(context, RemoteViewsRoot(1).apply {
+            children += EmittableBox().apply {
+                modifier = GlanceModifier.then(ActionModifier(LambdaAction("123") {
+                    didRunFirst = true
+                }))
+            }
+            children += EmittableBox().apply {
+                modifier = GlanceModifier.then(ActionModifier(LambdaAction("123") {
+                    didRunSecond = true
+                }))
+            }
+        })
+        session.processEvent(context, AppWidgetSession.RunLambda("123+0"))
+        assertTrue(didRunFirst)
+        assertFalse(didRunSecond)
+
+        didRunFirst = false
+        session.processEvent(context, AppWidgetSession.RunLambda("123+1"))
+        assertTrue(didRunSecond)
+        assertFalse(didRunFirst)
+    }
+
+    @Test
+    fun runLambda() = runTest {
+        var didRunFirst = false
+        var didRunSecond = false
+        session.processEmittableTree(context, RemoteViewsRoot(1).apply {
+            children += EmittableBox().apply {
+                modifier = GlanceModifier.then(ActionModifier(LambdaAction("123") {
+                    didRunFirst = true
+                }))
+            }
+            children += EmittableBox().apply {
+                modifier = GlanceModifier.then(ActionModifier(LambdaAction("123") {
+                    didRunSecond = true
+                    this@runTest.launch { session.close() }
+                }))
+            }
+        })
+
+        session.runLambda("123+0")
+        session.runLambda("123+1")
+        session.receiveEvents(context) {}
+        assertTrue(didRunFirst)
+        assertTrue(didRunSecond)
+    }
+
     private class SampleGlanceAppWidget(
         val ui: @Composable () -> Unit,
     ) : GlanceAppWidget() {
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/LambdaActionTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/LambdaActionTest.kt
new file mode 100644
index 0000000..3c2d2ed
--- /dev/null
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/LambdaActionTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 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.glance.appwidget
+
+import androidx.glance.GlanceModifier
+import androidx.glance.action.clickable
+import androidx.glance.layout.Box
+import androidx.glance.text.Text
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class LambdaActionTest {
+    @Test
+    fun siblingActionsHaveDifferentKeys() = runTest {
+        val lambdas = runTestingComposition {
+            Box {
+                Text("hello1", modifier = GlanceModifier.clickable {})
+                Text("hello2", modifier = GlanceModifier.clickable {})
+            }
+            Text("hello3", modifier = GlanceModifier.clickable {})
+        }.updateLambdaActionKeys()
+
+        assertThat(lambdas.size).isEqualTo(3)
+    }
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt
index b3073b3..734a6eb 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt
@@ -30,7 +30,7 @@
 import androidx.glance.appwidget.configurationContext
 import androidx.glance.appwidget.findViewByType
 import androidx.glance.appwidget.runAndTranslate
-import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.color.ColorProvider
 import androidx.glance.unit.FixedColorProvider
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/RadioButtonTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/RadioButtonTranslatorTest.kt
index 8c645ee..1f249fc 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/RadioButtonTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/RadioButtonTranslatorTest.kt
@@ -32,7 +32,7 @@
 import androidx.glance.appwidget.findView
 import androidx.glance.appwidget.findViewByType
 import androidx.glance.appwidget.runAndTranslate
-import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.color.ColorProvider
 import androidx.glance.unit.ColorProvider
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt
index 89d3739..3b70ee7 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt
@@ -30,7 +30,7 @@
 import androidx.glance.appwidget.findView
 import androidx.glance.appwidget.runAndTranslate
 import androidx.glance.appwidget.switchColors
-import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.color.ColorProvider
 import androidx.glance.unit.ColorProvider
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt
index 800d6c2..0d26478 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt
@@ -42,7 +42,7 @@
 import androidx.glance.appwidget.runAndTranslateInRtl
 import androidx.glance.appwidget.test.R
 import androidx.glance.appwidget.toPixels
-import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.color.ColorProvider
 import androidx.glance.layout.Column
 import androidx.glance.layout.fillMaxWidth
 import androidx.glance.text.FontStyle
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/unit/ColorProviderTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/unit/ColorProviderTest.kt
index d52ed7e..55f242d 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/unit/ColorProviderTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/unit/ColorProviderTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
 import androidx.glance.appwidget.ColorSubject.Companion.assertThat
+import androidx.glance.color.ColorProvider
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.annotation.Config
diff --git a/glance/glance-material/api/current.txt b/glance/glance-material/api/current.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/current.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+  public final class MaterialThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+  }
+
+}
+
diff --git a/glance/glance-material/api/public_plus_experimental_current.txt b/glance/glance-material/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/public_plus_experimental_current.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+  public final class MaterialThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+  }
+
+}
+
diff --git a/glance/glance-material/api/res-current.txt b/glance/glance-material/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-material/api/res-current.txt
diff --git a/glance/glance-material/api/restricted_current.txt b/glance/glance-material/api/restricted_current.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/restricted_current.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+  public final class MaterialThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+  }
+
+}
+
diff --git a/glance/glance-material/build.gradle b/glance/glance-material/build.gradle
new file mode 100644
index 0000000..58668cf
--- /dev/null
+++ b/glance/glance-material/build.gradle
@@ -0,0 +1,36 @@
+import androidx.build.AndroidXComposePlugin
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXComposePlugin")
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+// Disable multi-platform; this will only be used on Android.
+AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project, /* isMultiplatformEnabled= */false)
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+    api("androidx.annotation:annotation:1.4.0")
+    api("androidx.compose.runtime:runtime:1.1.1")
+    api(project(":glance:glance"))
+    implementation 'androidx.compose.material:material:1.3.0'
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 21
+    }
+    namespace "androidx.glance.material"
+}
+
+androidx {
+    name = "Android Glance Material"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenGroup = LibraryGroups.GLANCE
+    inceptionYear = "2022"
+    description = "Glance Material 2 integration library." +
+            " This library provides interop APIs with Material 2."
+}
+
diff --git a/glance/glance-material/src/androidMain/kotlin/androidx/glance/material/MaterialThemes.kt b/glance/glance-material/src/androidMain/kotlin/androidx/glance/material/MaterialThemes.kt
new file mode 100644
index 0000000..8ef025e
--- /dev/null
+++ b/glance/glance-material/src/androidMain/kotlin/androidx/glance/material/MaterialThemes.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2022 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.glance.material
+
+import androidx.compose.material.Colors
+import androidx.compose.material.primarySurface
+import androidx.compose.ui.graphics.Color
+import androidx.glance.GlanceTheme
+import androidx.glance.color.ColorProvider
+import androidx.glance.color.ColorProviders
+import androidx.glance.color.colorProviders
+import androidx.glance.unit.ColorProvider
+
+/**
+ * Given  Material [Colors], creates a [ColorProviders] that can be passed to [GlanceTheme]
+ */
+fun ColorProviders(
+    light: Colors,
+    dark: Colors
+): ColorProviders {
+
+    val background = ColorProvider(light.background, dark.background)
+    val  dark.onBackground)
+    val primary = ColorProvider(light.primary, dark.primary)
+    val  dark.onPrimary)
+    val surface = ColorProvider(light.primarySurface, dark.primarySurface)
+    val  dark.onSurface)
+    val secondary = ColorProvider(light.secondary, dark.secondary)
+    val  dark.onSecondary)
+    val error = ColorProvider(light.error, dark.error)
+    val  dark.onError)
+
+    return colorProviders(
+        primary = primary,
+        >
+        surface = surface,
+        >
+        secondary = secondary,
+        >
+        error = error,
+        >
+        background = background,
+        >
+        primaryContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        secondaryContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        tertiary = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        tertiaryContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        errorContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        surfaceVariant = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        outline = ColorProvider(ColorNotDefined, ColorNotDefined),
+        inverseOnSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
+        inverseSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
+        inversePrimary = ColorProvider(ColorNotDefined, ColorNotDefined),
+    )
+}
+
+/**
+ * Given  Material [Colors], creates a [ColorProviders] that can be passed to [GlanceTheme]
+ */
+fun ColorProviders(
+    colors: Colors
+): ColorProviders {
+
+    val background = ColorProvider(colors.background)
+    val >
+    val primary = ColorProvider(colors.primary)
+    val >
+    val surface = ColorProvider(colors.primarySurface)
+    val >
+    val secondary = ColorProvider(colors.secondary)
+    val >
+    val error = ColorProvider(colors.error)
+    val >
+
+    return colorProviders(
+        primary = primary,
+        >
+        surface = surface,
+        >
+        secondary = secondary,
+        >
+        error = error,
+        >
+        background = background,
+        >
+        primaryContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        secondaryContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        tertiary = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        tertiaryContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        errorContainer = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        surfaceVariant = ColorProvider(ColorNotDefined, ColorNotDefined),
+         ColorNotDefined),
+        outline = ColorProvider(ColorNotDefined, ColorNotDefined),
+        inverseOnSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
+        inverseSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
+        inversePrimary = ColorProvider(ColorNotDefined, ColorNotDefined),
+    )
+}
+
+private val ColorNotDefined = Color.Magenta
diff --git a/glance/glance-material3/api/current.txt b/glance/glance-material3/api/current.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/current.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+  public final class Material3ThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+  }
+
+}
+
diff --git a/glance/glance-material3/api/public_plus_experimental_current.txt b/glance/glance-material3/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/public_plus_experimental_current.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+  public final class Material3ThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+  }
+
+}
+
diff --git a/glance/glance-material3/api/res-current.txt b/glance/glance-material3/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-material3/api/res-current.txt
diff --git a/glance/glance-material3/api/restricted_current.txt b/glance/glance-material3/api/restricted_current.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/restricted_current.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+  public final class Material3ThemesKt {
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+    method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+  }
+
+}
+
diff --git a/glance/glance-material3/build.gradle b/glance/glance-material3/build.gradle
new file mode 100644
index 0000000..393b4f2
--- /dev/null
+++ b/glance/glance-material3/build.gradle
@@ -0,0 +1,36 @@
+import androidx.build.AndroidXComposePlugin
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXComposePlugin")
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+// Disable multi-platform; this will only be used on Android.
+AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project, /* isMultiplatformEnabled= */false)
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+    api("androidx.annotation:annotation:1.4.0")
+    api("androidx.compose.runtime:runtime:1.1.1")
+    api(project(":glance:glance"))
+    implementation "androidx.compose.material3:material3:1.0.0"
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 21
+    }
+    namespace "androidx.glance.material3"
+}
+
+androidx {
+    name = "Android Glance Material"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenGroup = LibraryGroups.GLANCE
+    inceptionYear = "2022"
+    description = "Glance Material integration library." +
+            " This library provides interop APIs with Material 3."
+}
+
diff --git a/glance/glance-material3/src/androidMain/kotlin/androidx/glance/material3/Material3Themes.kt b/glance/glance-material3/src/androidMain/kotlin/androidx/glance/material3/Material3Themes.kt
new file mode 100644
index 0000000..ad4718f
--- /dev/null
+++ b/glance/glance-material3/src/androidMain/kotlin/androidx/glance/material3/Material3Themes.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2022 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.glance.material3
+
+import androidx.compose.material3.ColorScheme
+import androidx.glance.color.ColorProvider
+import androidx.glance.color.ColorProviders
+import androidx.glance.color.colorProviders
+import androidx.glance.unit.ColorProvider
+
+/**
+ * Creates a Material 3 [ColorProviders] given a light and dark [ColorScheme]. Each color in the
+ * theme will have a day and night mode.
+ */
+fun ColorProviders(light: ColorScheme, dark: ColorScheme): ColorProviders {
+    return colorProviders(
+        primary = ColorProvider(day = light.primary, night = dark.primary),
+         = light.onPrimary, night = dark.onPrimary),
+        primaryContainer = ColorProvider(
+            day = light.primaryContainer,
+            night = dark.primaryContainer
+        ),
+        >
+            day = light.onPrimaryContainer,
+            night = dark.onPrimaryContainer
+        ),
+        secondary = ColorProvider(day = light.secondary, night = dark.secondary),
+         = light.onSecondary, night = dark.onSecondary),
+        secondaryContainer = ColorProvider(
+            day = light.secondaryContainer,
+            night = dark.secondaryContainer
+        ),
+        >
+            day = light.onSecondaryContainer,
+            night = dark.onSecondaryContainer
+        ),
+        tertiary = ColorProvider(day = light.tertiary, night = dark.tertiary),
+         = light.onTertiary, night = dark.onTertiary),
+        tertiaryContainer = ColorProvider(
+            day = light.tertiaryContainer,
+            night = dark.tertiaryContainer
+        ),
+        >
+            day = light.onTertiaryContainer,
+            night = dark.onTertiaryContainer
+        ),
+        error = ColorProvider(day = light.error, night = dark.error),
+        errorContainer = ColorProvider(day = light.errorContainer, night = dark.errorContainer),
+         = light.onError, night = dark.onError),
+        >
+            day = light.onErrorContainer,
+            night = dark.onErrorContainer
+        ),
+        background = ColorProvider(day = light.background, night = dark.background),
+         = light.onBackground, night = dark.onBackground),
+        surface = ColorProvider(day = light.surface, night = dark.surface),
+         = light.onSurface, night = dark.onSurface),
+        surfaceVariant = ColorProvider(day = light.surfaceVariant, night = dark.surfaceVariant),
+        >
+            day = light.onSurfaceVariant,
+            night = dark.onSurfaceVariant
+        ),
+        outline = ColorProvider(day = light.outline, night = dark.outline),
+        inverseOnSurface = ColorProvider(
+            day = light.inverseOnSurface,
+            night = dark.inverseOnSurface
+        ),
+        inverseSurface = ColorProvider(day = light.inverseSurface, night = dark.inverseSurface),
+        inversePrimary = ColorProvider(day = light.inversePrimary, night = dark.inversePrimary),
+    )
+}
+
+/**
+ * Creates a Material 3 [ColorProviders] given a [ColorScheme]. This is a fixed scheme and does not
+ * have day/night modes.
+ */
+fun ColorProviders(scheme: ColorScheme): ColorProviders {
+    return colorProviders(
+        primary = ColorProvider(color = scheme.primary),
+        >
+        primaryContainer = ColorProvider(color = scheme.primaryContainer),
+         = scheme.onPrimaryContainer),
+        secondary = ColorProvider(color = scheme.secondary),
+         = scheme.onSecondary),
+        secondaryContainer = ColorProvider(color = scheme.secondaryContainer),
+         = scheme.onSecondaryContainer),
+        tertiary = ColorProvider(color = scheme.tertiary),
+         = scheme.onTertiary),
+        tertiaryContainer = ColorProvider(color = scheme.tertiaryContainer),
+         = scheme.onTertiaryContainer),
+        error = ColorProvider(color = scheme.error),
+         = scheme.onError),
+        errorContainer = ColorProvider(color = scheme.errorContainer),
+         = scheme.onErrorContainer),
+        background = ColorProvider(color = scheme.background),
+         = scheme.onBackground),
+        surface = ColorProvider(color = scheme.surface),
+         = scheme.onSurface),
+        surfaceVariant = ColorProvider(color = scheme.surfaceVariant),
+         = scheme.onSurfaceVariant),
+        outline = ColorProvider(color = scheme.outline),
+        inverseOnSurface = ColorProvider(color = scheme.inverseOnSurface),
+        inverseSurface = ColorProvider(color = scheme.inverseSurface),
+        inversePrimary = ColorProvider(color = scheme.inversePrimary),
+    )
+}
\ No newline at end of file
diff --git a/glance/glance-wear-tiles/src/androidMain/kotlin/androidx/glance/wear/tiles/WearCompositionTranslator.kt b/glance/glance-wear-tiles/src/androidMain/kotlin/androidx/glance/wear/tiles/WearCompositionTranslator.kt
index cec3b08..7e0ee8d 100644
--- a/glance/glance-wear-tiles/src/androidMain/kotlin/androidx/glance/wear/tiles/WearCompositionTranslator.kt
+++ b/glance/glance-wear-tiles/src/androidMain/kotlin/androidx/glance/wear/tiles/WearCompositionTranslator.kt
@@ -34,6 +34,7 @@
 import androidx.glance.VisibilityModifier
 import androidx.glance.action.Action
 import androidx.glance.action.ActionModifier
+import androidx.glance.action.LambdaAction
 import androidx.glance.wear.tiles.action.RunCallbackAction
 import androidx.glance.action.StartActivityAction
 import androidx.glance.action.StartActivityClassAction
@@ -213,6 +214,13 @@
             builder.setOnClick(ActionBuilders.LoadAction.Builder().build())
                 .setId(callbackClass.canonicalName!!)
         }
+        is LambdaAction -> {
+            Log.e(
+                GlanceWearTileTag,
+                "Lambda actions are not currently supported on Wear Tiles. Use " +
+                    "actionRunCallback actions instead."
+            )
+        }
         else -> {
             Log.e(GlanceWearTileTag, "Unknown Action $this, skipped")
         }
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index bc9a2a2..ad6cf59 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -18,6 +18,7 @@
 
   public final class ButtonKt {
     method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
   }
 
   public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
@@ -117,6 +118,7 @@
 
   public final class ActionKt {
     method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+    method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public abstract class ActionParameters {
@@ -145,6 +147,10 @@
     method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
   }
 
+  public final class LambdaActionKt {
+    method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
   public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
     method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
     method public void clear();
@@ -225,6 +231,10 @@
     method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
   }
 
+  public final class DayNightColorProvidersKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+  }
+
 }
 
 package androidx.glance.layout {
diff --git a/glance/glance/api/public_plus_experimental_current.txt b/glance/glance/api/public_plus_experimental_current.txt
index bc9a2a2..ad6cf59 100644
--- a/glance/glance/api/public_plus_experimental_current.txt
+++ b/glance/glance/api/public_plus_experimental_current.txt
@@ -18,6 +18,7 @@
 
   public final class ButtonKt {
     method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
   }
 
   public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
@@ -117,6 +118,7 @@
 
   public final class ActionKt {
     method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+    method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public abstract class ActionParameters {
@@ -145,6 +147,10 @@
     method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
   }
 
+  public final class LambdaActionKt {
+    method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
   public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
     method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
     method public void clear();
@@ -225,6 +231,10 @@
     method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
   }
 
+  public final class DayNightColorProvidersKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+  }
+
 }
 
 package androidx.glance.layout {
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index bc9a2a2..ad6cf59 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -18,6 +18,7 @@
 
   public final class ButtonKt {
     method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
   }
 
   public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
@@ -117,6 +118,7 @@
 
   public final class ActionKt {
     method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+    method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public abstract class ActionParameters {
@@ -145,6 +147,10 @@
     method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
   }
 
+  public final class LambdaActionKt {
+    method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
   public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
     method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
     method public void clear();
@@ -225,6 +231,10 @@
     method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
   }
 
+  public final class DayNightColorProvidersKt {
+    method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+  }
+
 }
 
 package androidx.glance.layout {
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/Button.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/Button.kt
index a41cba2..19116de 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/Button.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/Button.kt
@@ -19,6 +19,7 @@
 import androidx.annotation.RestrictTo
 import androidx.compose.runtime.Composable
 import androidx.glance.action.Action
+import androidx.glance.action.action
 import androidx.glance.action.clickable
 import androidx.glance.text.EmittableText
 import androidx.glance.text.TextStyle
@@ -45,6 +46,40 @@
     style: TextStyle? = null,
     colors: ButtonColors = defaultButtonColors(),
     maxLines: Int = Int.MAX_VALUE,
+) = ButtonElement(text, onClick, modifier, enabled, style, colors, maxLines)
+
+/**
+ * Adds a button view to the glance view.
+ *
+ * @param text The text that this button will show.
+ * @param onClick The action to be performed when this button is clicked.
+ * @param modifier The modifier to be applied to this button.
+ * @param enabled If false, the button will not be clickable.
+ * @param style The style to be applied to the text in this button.
+ * @param colors The colors to use for the background and content of the button.
+ * @param maxLines An optional maximum number of lines for the text to span, wrapping if
+ * necessary. If the text exceeds the given number of lines, it will be truncated.
+ */
+@Composable
+fun Button(
+    text: String,
+    onClick: () -> Unit,
+    modifier: GlanceModifier = GlanceModifier,
+    enabled: Boolean = true,
+    style: TextStyle? = null,
+    colors: ButtonColors = defaultButtonColors(),
+    maxLines: Int = Int.MAX_VALUE,
+) = ButtonElement(text, action(block = onClick), modifier, enabled, style, colors, maxLines)
+
+@Composable
+internal fun ButtonElement(
+    text: String,
+    onClick: Action,
+    modifier: GlanceModifier = GlanceModifier,
+    enabled: Boolean = true,
+    style: TextStyle? = null,
+    colors: ButtonColors = defaultButtonColors(),
+    maxLines: Int = Int.MAX_VALUE,
 ) {
     var finalModifier = if (enabled) modifier.clickable(onClick) else modifier
     finalModifier = finalModifier.background(colors.backgroundColor)
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/action/Action.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/action/Action.kt
index 28a806e..d301e87 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/action/Action.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/action/Action.kt
@@ -18,6 +18,7 @@
 
 import android.app.Activity
 import androidx.annotation.RestrictTo
+import androidx.compose.runtime.Composable
 import androidx.glance.GlanceModifier
 
 /**
@@ -33,6 +34,13 @@
 fun GlanceModifier.clickable(onClick: Action): GlanceModifier =
     this.then(ActionModifier(onClick))
 
+/**
+ * Run [block] in response to a user click.
+ */
+@Composable
+fun GlanceModifier.clickable(block: () -> Unit): GlanceModifier =
+    this.then(ActionModifier(action(block = block)))
+
 /** @suppress */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class ActionModifier(val action: Action) : GlanceModifier.Element {
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/action/LambdaAction.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/action/LambdaAction.kt
new file mode 100644
index 0000000..8076cd7
--- /dev/null
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/action/LambdaAction.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 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.glance.action
+
+import androidx.annotation.RestrictTo
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.currentCompositeKeyHash
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class LambdaAction(
+    val key: String,
+    val block: () -> Unit,
+) : Action {
+    override fun toString() = "LambdaAction($key, ${block.hashCode()})"
+}
+
+/**
+ * Create an [Action] that runs [block] when triggered.
+ *
+ * @param key An optional key to be used as a key for the action. If not provided we use the key
+ * that is automatically generated by the Compose runtime which is unique for every exact code
+ * location in the composition tree.
+ * @param block the function to be run when this action is triggered.
+ */
+@Composable
+fun action(
+    key: String? = null,
+    block: () -> Unit,
+): Action {
+    val finalKey = if (!key.isNullOrEmpty()) key else currentCompositeKeyHash.toString()
+    return LambdaAction(finalKey, block)
+}
\ No newline at end of file
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/color/DayNightColorProviders.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/color/DayNightColorProviders.kt
new file mode 100644
index 0000000..aa1e76c
--- /dev/null
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/color/DayNightColorProviders.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 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.glance.color
+
+import android.content.Context
+import android.content.res.Configuration
+import androidx.annotation.RestrictTo
+import androidx.compose.ui.graphics.Color
+import androidx.glance.unit.ColorProvider
+
+/**
+ * Returns a [ColorProvider] that provides [day] when night mode is off, and [night] when night
+ * mode is on.
+ */
+fun ColorProvider(day: Color, night: Color): ColorProvider {
+    return DayNightColorProvider(day, night)
+}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+data class DayNightColorProvider(val day: Color, val night: Color) : ColorProvider {
+    override fun getColor(context: Context) = getColor(isNightMode = context.isNightMode)
+
+    fun getColor(isNightMode: Boolean) = if (isNightMode) night else day
+}
+
+val Context.isNightMode: Boolean
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    get() = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
+        Configuration.UI_MODE_NIGHT_YES
\ No newline at end of file
diff --git a/health/connect/connect-client/api/current.txt b/health/connect/connect-client/api/current.txt
index 081ecbd..bff896d 100644
--- a/health/connect/connect-client/api/current.txt
+++ b/health/connect/connect-client/api/current.txt
@@ -1120,7 +1120,7 @@
   }
 
   public final class StepsRecord implements androidx.health.connect.client.records.Record {
-    ctor public StepsRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, long count, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public StepsRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, @IntRange(from=1L, to=1000000L) long count, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public long getCount();
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
diff --git a/health/connect/connect-client/api/public_plus_experimental_current.txt b/health/connect/connect-client/api/public_plus_experimental_current.txt
index 081ecbd..bff896d 100644
--- a/health/connect/connect-client/api/public_plus_experimental_current.txt
+++ b/health/connect/connect-client/api/public_plus_experimental_current.txt
@@ -1120,7 +1120,7 @@
   }
 
   public final class StepsRecord implements androidx.health.connect.client.records.Record {
-    ctor public StepsRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, long count, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public StepsRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, @IntRange(from=1L, to=1000000L) long count, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public long getCount();
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
diff --git a/health/connect/connect-client/api/restricted_current.txt b/health/connect/connect-client/api/restricted_current.txt
index a9c1a0f..0bf9f664 100644
--- a/health/connect/connect-client/api/restricted_current.txt
+++ b/health/connect/connect-client/api/restricted_current.txt
@@ -1143,7 +1143,7 @@
   }
 
   public final class StepsRecord implements androidx.health.connect.client.records.IntervalRecord {
-    ctor public StepsRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, long count, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public StepsRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, @IntRange(from=1L, to=1000000L) long count, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public long getCount();
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt
index 6a0e2eb..4963871 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt
@@ -15,6 +15,7 @@
  */
 package androidx.health.connect.client.records
 
+import androidx.annotation.IntRange
 import androidx.health.connect.client.aggregate.AggregateMetric
 import androidx.health.connect.client.records.metadata.Metadata
 import java.time.Instant
@@ -35,11 +36,12 @@
     override val endTime: Instant,
     override val endZoneOffset: ZoneOffset?,
     /** Count. Required field. Valid range: 1-1000000. */
+    @IntRange(from = 1, to = 1000_000)
     public val count: Long,
     override val metadata: Metadata = Metadata.EMPTY,
 ) : IntervalRecord {
     init {
-        requireNonNegative(value = count, name = "count")
+        count.requireNotLess(other = 1, name = "count")
         count.requireNotMore(other = 1000_000, name = "count")
         require(startTime.isBefore(endTime)) { "startTime must be before endTime." }
     }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt
index 79718d5..8e7d4a2 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt
@@ -60,4 +60,30 @@
             )
         }
     }
+
+    @Test
+    fun invalidSteps_tooFewCount_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            StepsRecord(
+                startTime = Instant.ofEpochMilli(1234L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1235L),
+                endZoneOffset = null,
+                count = 0,
+            )
+        }
+    }
+
+    @Test
+    fun invalidSteps_tooLargeCount_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            StepsRecord(
+                startTime = Instant.ofEpochMilli(1234L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1235L),
+                endZoneOffset = null,
+                count = 1000_001,
+            )
+        }
+    }
 }
diff --git a/health/health-services-client/api/1.0.0-beta02.txt b/health/health-services-client/api/1.0.0-beta02.txt
index e37c668..590e802 100644
--- a/health/health-services-client/api/1.0.0-beta02.txt
+++ b/health/health-services-client/api/1.0.0-beta02.txt
@@ -19,6 +19,22 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startExerciseAsync(androidx.health.services.client.data.ExerciseConfig configuration);
   }
 
+  public final class ExerciseClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? addGoalToActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearUpdateCallback(androidx.health.services.client.ExerciseClient, androidx.health.services.client.ExerciseUpdateCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? endExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? resumeExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? startExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
@@ -55,6 +71,11 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
+  public final class MeasureClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.MeasureClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.MeasureCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? unregisterMeasureCallback(androidx.health.services.client.MeasureClient, androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
@@ -85,6 +106,14 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> setPassiveListenerServiceAsync(Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config);
   }
 
+  public final class PassiveMonitoringClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerCallback(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.PassiveMonitoringCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? setPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
 }
 
 package androidx.health.services.client.data {
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index e37c668..590e802 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -19,6 +19,22 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startExerciseAsync(androidx.health.services.client.data.ExerciseConfig configuration);
   }
 
+  public final class ExerciseClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? addGoalToActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearUpdateCallback(androidx.health.services.client.ExerciseClient, androidx.health.services.client.ExerciseUpdateCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? endExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? resumeExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? startExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
@@ -55,6 +71,11 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
+  public final class MeasureClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.MeasureClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.MeasureCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? unregisterMeasureCallback(androidx.health.services.client.MeasureClient, androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
@@ -85,6 +106,14 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> setPassiveListenerServiceAsync(Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config);
   }
 
+  public final class PassiveMonitoringClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerCallback(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.PassiveMonitoringCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? setPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
 }
 
 package androidx.health.services.client.data {
diff --git a/health/health-services-client/api/public_plus_experimental_1.0.0-beta02.txt b/health/health-services-client/api/public_plus_experimental_1.0.0-beta02.txt
index e37c668..590e802 100644
--- a/health/health-services-client/api/public_plus_experimental_1.0.0-beta02.txt
+++ b/health/health-services-client/api/public_plus_experimental_1.0.0-beta02.txt
@@ -19,6 +19,22 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startExerciseAsync(androidx.health.services.client.data.ExerciseConfig configuration);
   }
 
+  public final class ExerciseClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? addGoalToActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearUpdateCallback(androidx.health.services.client.ExerciseClient, androidx.health.services.client.ExerciseUpdateCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? endExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? resumeExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? startExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
@@ -55,6 +71,11 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
+  public final class MeasureClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.MeasureClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.MeasureCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? unregisterMeasureCallback(androidx.health.services.client.MeasureClient, androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
@@ -85,6 +106,14 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> setPassiveListenerServiceAsync(Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config);
   }
 
+  public final class PassiveMonitoringClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerCallback(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.PassiveMonitoringCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? setPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
 }
 
 package androidx.health.services.client.data {
diff --git a/health/health-services-client/api/public_plus_experimental_current.txt b/health/health-services-client/api/public_plus_experimental_current.txt
index e37c668..590e802 100644
--- a/health/health-services-client/api/public_plus_experimental_current.txt
+++ b/health/health-services-client/api/public_plus_experimental_current.txt
@@ -19,6 +19,22 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startExerciseAsync(androidx.health.services.client.data.ExerciseConfig configuration);
   }
 
+  public final class ExerciseClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? addGoalToActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearUpdateCallback(androidx.health.services.client.ExerciseClient, androidx.health.services.client.ExerciseUpdateCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? endExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? resumeExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? startExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
@@ -55,6 +71,11 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
+  public final class MeasureClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.MeasureClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.MeasureCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? unregisterMeasureCallback(androidx.health.services.client.MeasureClient, androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
@@ -85,6 +106,14 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> setPassiveListenerServiceAsync(Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config);
   }
 
+  public final class PassiveMonitoringClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerCallback(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.PassiveMonitoringCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? setPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
 }
 
 package androidx.health.services.client.data {
diff --git a/health/health-services-client/api/restricted_1.0.0-beta02.txt b/health/health-services-client/api/restricted_1.0.0-beta02.txt
index e37c668..590e802 100644
--- a/health/health-services-client/api/restricted_1.0.0-beta02.txt
+++ b/health/health-services-client/api/restricted_1.0.0-beta02.txt
@@ -19,6 +19,22 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startExerciseAsync(androidx.health.services.client.data.ExerciseConfig configuration);
   }
 
+  public final class ExerciseClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? addGoalToActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearUpdateCallback(androidx.health.services.client.ExerciseClient, androidx.health.services.client.ExerciseUpdateCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? endExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? resumeExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? startExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
@@ -55,6 +71,11 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
+  public final class MeasureClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.MeasureClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.MeasureCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? unregisterMeasureCallback(androidx.health.services.client.MeasureClient, androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
@@ -85,6 +106,14 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> setPassiveListenerServiceAsync(Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config);
   }
 
+  public final class PassiveMonitoringClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerCallback(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.PassiveMonitoringCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? setPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
 }
 
 package androidx.health.services.client.data {
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index e37c668..590e802 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -19,6 +19,22 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startExerciseAsync(androidx.health.services.client.data.ExerciseConfig configuration);
   }
 
+  public final class ExerciseClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? addGoalToActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearUpdateCallback(androidx.health.services.client.ExerciseClient, androidx.health.services.client.ExerciseUpdateCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? endExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? resumeExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? startExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseConfig configuration, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
@@ -55,6 +71,11 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
+  public final class MeasureClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.MeasureClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.MeasureCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? unregisterMeasureCallback(androidx.health.services.client.MeasureClient, androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
@@ -85,6 +106,14 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> setPassiveListenerServiceAsync(Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config);
   }
 
+  public final class PassiveMonitoringClientExtensionKt {
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerCallback(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? clearPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? flush(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? getCapabilities(androidx.health.services.client.PassiveMonitoringClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.PassiveMonitoringCapabilities>) throws android.os.RemoteException;
+    method @kotlin.jvm.Throws(exceptionClasses=android.os.RemoteException::class) public static suspend Object? setPassiveListenerService(androidx.health.services.client.PassiveMonitoringClient, Class<? extends androidx.health.services.client.PassiveListenerService> service, androidx.health.services.client.data.PassiveListenerConfig config, kotlin.coroutines.Continuation<? super java.lang.Void>) throws android.os.RemoteException;
+  }
+
 }
 
 package androidx.health.services.client.data {
diff --git a/health/health-services-client/build.gradle b/health/health-services-client/build.gradle
index 138d521..1e6d812 100644
--- a/health/health-services-client/build.gradle
+++ b/health/health-services-client/build.gradle
@@ -25,10 +25,12 @@
 
 dependencies {
     api(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesCore)
     api("androidx.annotation:annotation:1.1.0")
     implementation(libs.guavaListenableFuture)
     implementation(libs.guavaAndroid)
     implementation("androidx.core:core-ktx:1.7.0")
+    implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     implementation(libs.protobufLite)
 
     testImplementation(libs.junit)
@@ -36,6 +38,9 @@
     testImplementation(libs.robolectric)
     testImplementation(libs.testCore)
     testImplementation(libs.truth)
+    testImplementation(libs.kotlinTest)
+    testImplementation(libs.kotlinCoroutinesTest)
+    testImplementation(libs.kotlinTest)
 }
 
 android {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt
index 59e76bd..fd572f3 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt
@@ -52,7 +52,8 @@
      * aggregation will occur until the exercise is started.
      *
      * If an app is actively preparing and another app starts tracking an active exercise then the
-     * preparing app should expect to receive an [ExerciseUpdate] with [ExerciseState.TERMINATED]
+     * preparing app should expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED] along
+     * with the reason [ExerciseEndReason.AUTO_END_SUPERSEDED] to the [ExerciseUpdateCallback]
      * indicating that their session has been superseded and ended. At that point no additional
      * updates to availability or data will be sent until the app calls prepareExercise again.
      *
@@ -69,15 +70,17 @@
      *
      * Since Health Services only allows a single active exercise at a time, this will terminate any
      * active exercise currently in progress before starting the new one. If this occurs, clients
-     * can expect to receive an [ExerciseUpdate] with [ExerciseState.TERMINATED], indicating that
-     * their exercise has been superseded and that no additional updates will be sent. Clients can
-     * use [getCurrentExerciseInfoAsync] (described below) to check if they or another app has an
-     * active exercise in-progress.
+     * can expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED] along with the reason
+     * [ExerciseEndReason.AUTO_END_SUPERSEDED] to the [ExerciseUpdateCallback] indicating that their
+     * exercise has been superseded and that no additional updates will be sent. Clients can use
+     * [getCurrentExerciseInfoAsync] (described below) to check if they or another app has an active
+     * exercise in-progress.
      *
      * If the client fails to maintain a live [ExerciseUpdateCallback] for at least five minutes
      * during the duration of the exercise, Health Services can decide to terminate the exercise. If
-     * this occurs, clients can expect to receive an [ExerciseUpdate] with
-     * [ExerciseState.AUTO_ENDED] indicating that their exercise has been automatically ended due to
+     * this occurs, clients can expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED]
+     * along with the reason [ExerciseEndReason.AUTO_END_MISSING_LISTENER] to the
+     * [ExerciseUpdateCallback] indicating that their exercise has been automatically ended due to
      * the lack of callback.
      *
      * Clients should only request [ExerciseType]s, [DataType]s, goals, and auto-pause enabled that
@@ -193,7 +196,8 @@
      * deliver them as soon as the callback is registered again. If the client fails to maintain a
      * live [ExerciseUpdateCallback] for at least five minutes during the duration of the exercise
      * Health Services can decide to terminate the exercise automatically. If this occurs, clients
-     * can expect to receive an [ExerciseUpdate] with [ExerciseState.AUTO_ENDED] indicating that
+     * can expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED] along with the reason
+     * [ExerciseEndReason.AUTO_END_MISSING_LISTENER] to the [ExerciseUpdateCallback] indicating that
      * their exercise has been automatically ended due to the lack of callback.
      *
      * Calls to the callback will be executed on the main application thread. To control where to
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt
new file mode 100644
index 0000000..2e54b70
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2022 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.health.services.client
+
+import androidx.concurrent.futures.await
+import androidx.health.services.client.data.DataPoint
+import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.ExerciseCapabilities
+import androidx.health.services.client.data.ExerciseConfig
+import androidx.health.services.client.data.ExerciseEndReason
+import androidx.health.services.client.data.ExerciseGoal
+import androidx.health.services.client.data.ExerciseInfo
+import androidx.health.services.client.data.ExerciseState
+import androidx.health.services.client.data.ExerciseType
+import androidx.health.services.client.data.ExerciseUpdate
+import androidx.health.services.client.data.WarmUpConfig
+
+/**
+ * Prepares for a new exercise.
+ *
+ * Once called, Health Services will warmup the sensors based on the [ExerciseType] and
+ * requested [DataType]s.
+ *
+ * If the calling app already has an active exercise in progress or if it does not have the
+ * required permissions, then this call throws [android.os.RemoteException]. If another app owns
+ * the active exercise then this call will succeed.
+ *
+ * Sensors available for warmup are GPS [DataType.LOCATION] and HeartRate
+ * [DataType.HEART_RATE_BPM]. Other [DataType]s requested for warmup based on exercise
+ * capabilities will be a no-op for the prepare stage.
+ *
+ * The DataType availability can be obtained through the
+ * [ExerciseUpdateCallback.onAvailabilityChanged] callback. [ExerciseUpdate]s with the supported
+ * DataType [DataPoint] will also be returned in the [ExerciseState.PREPARING] state, though no
+ * aggregation will occur until the exercise is started.
+ *
+ * If an app is actively preparing and another app starts tracking an active exercise then the
+ * preparing app should expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED] along with
+ * the reason [ExerciseEndReason.AUTO_END_SUPERSEDED] to the [ExerciseUpdateCallback] indicating
+ * that their session has been superseded and ended. At that point no additional updates to
+ * availability or data will be sent until the app calls prepareExercise again.
+ *
+ * @param configuration the [WarmUpConfig] containing the desired exercise and data types
+ * @throws [android.os.RemoteException] if the calling app already has an active exercise in
+ * progress or if it does not have the required permissions or if Health Service fails to process
+ * the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.prepareExercise(configuration: WarmUpConfig) =
+    prepareExerciseAsync(configuration).await()
+
+/**
+ * Starts a new exercise.
+ *
+ * Once started, Health Services will begin collecting data associated with the exercise.
+ *
+ * Since Health Services only allows a single active exercise at a time, this will terminate any
+ * active exercise currently in progress before starting the new one. If this occurs, clients
+ * can expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED], indicating that
+ * their exercise has been superseded and that no additional updates will be sent. Clients can
+ * use [getCurrentExerciseInfo] (described below) to check if they or another app has an
+ * active exercise in-progress.
+ *
+ * If the client fails to maintain a live [ExerciseUpdateCallback] for at least five minutes
+ * during the duration of the exercise, Health Services can decide to terminate the exercise. If
+ * this occurs, clients can expect to receive an [ExerciseUpdate] with [ExerciseState.ENDED] along
+ * with the reason [ExerciseEndReason.AUTO_END_MISSING_LISTENER] to the [ExerciseUpdateCallback]
+ * indicating that their exercise has been automatically ended due to the lack of callback.
+ *
+ * Clients should only request [ExerciseType]s, [DataType]s, goals, and auto-pause enabled that
+ * matches the [ExerciseCapabilities] returned by [getCapabilities] since Health Services
+ * will reject requests asking for unsupported configurations.
+ *
+ * @param configuration the [ExerciseConfig] describing this exercise
+ * @throws [android.os.RemoteException] if Health Service fails to process the call or if it does
+ * not have the required permissions
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.startExercise(configuration: ExerciseConfig) =
+    startExerciseAsync(configuration).await()
+
+/**
+ * Pauses the current exercise, if it is currently started.
+ *
+ * Before transitioning to [ExerciseState.USER_PAUSED], Health Services will flush and return
+ * the sensor data. While the exercise is paused, active time and cumulative metrics such as
+ * distance will not accumulate. Instantaneous measurements such as speed and heart rate will
+ * continue to update if requested in the [ExerciseConfig].
+ *
+ * Note that GPS and other sensors may be stopped when the exercise is paused in order to
+ * conserve battery. This may happen immediately, or after some time. (The exact behavior is
+ * hardware dependent.) Should this happen, access will automatically resume when the exercise
+ * is resumed.
+ *
+ * If the exercise is already paused then this method has no effect. If the exercise has ended
+ * then [android.os.RemoteException] is thrown.
+ *
+ * @throws [android.os.RemoteException] if the exercise has ended or if Health Service fails to
+ * process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.pauseExercise() = pauseExerciseAsync().await()
+
+/**
+ * Resumes the current exercise, if it is currently paused.
+ *
+ * Once resumed active time and cumulative metrics such as distance will resume accumulating.
+ *
+ * If the exercise has been started but is not currently paused this method has no effect. If
+ * the exercise has ended then [android.os.RemoteException] is thrown.
+ *
+ * @throws [android.os.RemoteException] if the exercise has ended or if Health Service fails to
+ * process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.resumeExercise() = resumeExerciseAsync().await()
+
+/**
+ * Ends the current exercise, if it has been started.
+ *
+ * Health Services will flush and then shut down the active sensors and return an
+ * [ExerciseUpdate] with [ExerciseState.ENDED] along with the reason
+ * [ExerciseEndReason.USER_END] to the [ExerciseUpdateCallback]. If the exercise has already
+ * ended then this call fails with a [android.os.RemoteException].
+ *
+ * No additional metrics will be produced for the exercise and any on device persisted data
+ * about the exercise will be deleted after the summary has been sent back.
+ *
+ * @throws [android.os.RemoteException] if exercise has already ended or if Health Service fails to
+ * process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.endExercise() = endExerciseAsync().await()
+
+/**
+ * Flushes the sensors for the active exercise. This call should be used sparingly and will be
+ * subject to throttling by Health Services.
+ *
+ * @throws [android.os.RemoteException] if the Health Service fails to process the request
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.flush() = flushAsync().await()
+
+/**
+ * Ends the current lap, calls [ExerciseUpdateCallback.onLapSummaryReceived] with data spanning
+ * the marked lap and starts a new lap. If the exercise supports laps this method can be called
+ * at any point after an exercise has been started and before it has been ended regardless of
+ * the exercise status.
+ *
+ * The metrics in the lap summary will start from either the start time of the exercise or the
+ * last time a lap was marked to the time this method is being called.
+ *
+ * @throws [android.os.RemoteException] If there's no exercise being tracked or if Health Service
+ * fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.markLap() = markLapAsync().await()
+
+/**
+ * Returns the current [ExerciseInfo].
+ *
+ * This can be used by clients to determine if they or another app already owns an active
+ * exercise being tracked by Health Services. For example, if an app is killed and it learns it
+ * owns the active exercise it can register a new [ExerciseUpdateCallback] and pick tracking up
+ * from where it left off.
+ *
+ * @return a [ExerciseInfo] that contains information about the current exercise
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.getCurrentExerciseInfo() = getCurrentExerciseInfoAsync().await()
+
+/**
+ * Clears the callback set using [ExerciseClient.setUpdateCallback].
+ *
+ * If this callback is not already registered then this will be a no-op.
+ *
+ * @param callback the [ExerciseUpdateCallback] to clear
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@Suppress("ExecutorRegistration")
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.clearUpdateCallback(callback: ExerciseUpdateCallback) =
+    clearUpdateCallbackAsync(callback).await()
+
+/**
+ * Adds an [ExerciseGoal] for an active exercise.
+ *
+ * Goals apply to only active exercises owned by the client, and will be invalidated once the
+ * exercise is complete.
+ *
+ * @param exerciseGoal the [ExerciseGoal] to add to this exercise
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.addGoalToActiveExercise(exerciseGoal: ExerciseGoal<*>) =
+    addGoalToActiveExerciseAsync(exerciseGoal).await()
+
+/**
+ * Removes an exercise goal for an active exercise.
+ *
+ * Takes into account equivalent milestones (i.e. milestones which are not equal but are
+ * different representation of a common milestone. e.g. milestone A for every 2kms, currently at
+ * threshold of 10kms, and milestone B for every 2kms, currently at threshold of 8kms).
+ *
+ * @param exerciseGoal the [ExerciseGoal] to remove from this exercise
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.removeGoalFromActiveExercise(
+    exerciseGoal: ExerciseGoal<*>
+) = removeGoalFromActiveExerciseAsync(exerciseGoal).await()
+
+/**
+ * Enables or disables auto pause/resume for the current exercise.
+ *
+ * @param enabled a boolean to indicate if should be enabled or disabled
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.overrideAutoPauseAndResumeForActiveExercise(
+    enabled: Boolean
+) = overrideAutoPauseAndResumeForActiveExerciseAsync(enabled).await()
+
+/**
+ * Returns the [ExerciseCapabilities] of this client for the device.
+ *
+ * This can be used to determine what [ExerciseType]s and [DataType]s this device supports.
+ * Clients should use the capabilities to inform their requests since Health Services will
+ * typically reject requests made for [DataType]s or features (such as auto-pause) which are not
+ * enabled for the rejected [ExerciseType].
+ *
+ * @return a the [ExerciseCapabilities] for this device
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun ExerciseClient.getCapabilities() = getCapabilitiesAsync().await()
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClientExtension.kt b/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClientExtension.kt
new file mode 100644
index 0000000..951ccd3
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClientExtension.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 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.health.services.client
+
+import androidx.concurrent.futures.await
+import androidx.health.services.client.data.DeltaDataType
+import androidx.health.services.client.data.MeasureCapabilities
+
+/**
+ * Unregisters the given [MeasureCallback] for updates of the given [DeltaDataType].
+ *
+ * @param dataType the [DeltaDataType] that needs to be unregistered
+ * @param callback the [MeasureCallback] which was used in registration
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@Suppress("PairedRegistration")
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun MeasureClient.unregisterMeasureCallback(
+    dataType: DeltaDataType<*, *>,
+    callback: MeasureCallback
+) = unregisterMeasureCallbackAsync(dataType, callback).await()
+
+/**
+ * Returns the [MeasureCapabilities] of this client for the device.
+ *
+ * This can be used to determine what [DeltaDataType]s this device supports for live
+ * measurement. Clients should use the capabilities to inform their requests since Health
+ * Services will typically reject requests made for [DeltaDataType]s which are not enabled for
+ * measurement.
+ *
+ * @return a [MeasureCapabilities] for this device
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun MeasureClient.getCapabilities() = getCapabilitiesAsync().await()
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/PassiveMonitoringClientExtension.kt b/health/health-services-client/src/main/java/androidx/health/services/client/PassiveMonitoringClientExtension.kt
new file mode 100644
index 0000000..78973c6
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/PassiveMonitoringClientExtension.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 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.health.services.client
+
+import androidx.concurrent.futures.await
+import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.PassiveListenerConfig
+import androidx.health.services.client.data.PassiveMonitoringCapabilities
+
+/**
+ * Subscribes for updates to be periodically delivered to the app.
+ *
+ * Data updates will be batched and delivered from the point of initial registration and will
+ * continue to be delivered until the [DataType] is unregistered, either by explicitly calling
+ * [clearPassiveListenerService] or by registering again without that [DataType]
+ * included in the request. Higher frequency updates are available through [ExerciseClient] or
+ * [MeasureClient]. Any requested goal, user activity, or health event updates will not be
+ * batched.
+ *
+ * Health Services will automatically bind to the provided [PassiveListenerService] to send the
+ * update. Clients are responsible for defining the service in their app manifest. They should
+ * also require the `com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING`
+ * permission in their app manifest service definition in order to ensure that Health Services
+ * is the source of the binding.
+ *
+ * This registration is unique per subscribing app. Subsequent registrations will replace the
+ * previous registration, if one had been made. The client is responsible for ensuring that
+ * their requested [PassiveListenerConfig] is supported on this device by checking the
+ * [PassiveMonitoringCapabilities]. The returned future will fail if the request is not
+ * supported on the current device or the client does not have the required permissions for the
+ * request.
+ *
+ * @param service the [PassiveListenerService] to bind to
+ * @param config the [PassiveListenerConfig] from the client
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun PassiveMonitoringClient.setPassiveListenerService(
+    service: Class<out PassiveListenerService>,
+    config: PassiveListenerConfig
+) = setPassiveListenerServiceAsync(service, config).await()
+
+/**
+ * Unregisters the subscription made by [setPassiveListenerService].
+ *
+ * Data will not be delivered after this call so if clients care about any pending batched data
+ * they should call flush before unregistering.
+ *
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun PassiveMonitoringClient.clearPassiveListenerService() =
+    clearPassiveListenerServiceAsync().await()
+
+/**
+ * Unregisters the subscription made by [PassiveMonitoringClient.setPassiveListenerCallback].
+ *
+ * Data will not be delivered after this call so if clients care about any pending batched data
+ * they should call flush before unregistering.
+ *
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun PassiveMonitoringClient.clearPassiveListenerCallback() =
+    clearPassiveListenerCallbackAsync().await()
+
+/**
+ * Flushes the sensors for the registered [DataType]s.
+ *
+ * If no listener has been registered by this client, this will be a no-op. This call should be
+ * used sparingly and will be subject to throttling by Health Services.
+ *
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun PassiveMonitoringClient.flush() = flushAsync().await()
+
+/**
+ * Returns the [PassiveMonitoringCapabilities] of this client for this device.
+ *
+ * This can be used to determine what [DataType]s this device supports for passive monitoring
+ * and goals. Clients should use the capabilities to inform their requests since Health Services
+ * will typically reject requests made for [DataType]s which are not supported.
+ *
+ * @return a [PassiveMonitoringCapabilities] for this device
+ * @throws [android.os.RemoteException] if Health Service fails to process the call
+ */
+@kotlin.jvm.Throws(android.os.RemoteException::class)
+public suspend fun PassiveMonitoringClient.getCapabilities() = getCapabilitiesAsync().await()
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/ExerciseInfoCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/ExerciseInfoCallback.kt
index b23d8bd..c2db7c4 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/ExerciseInfoCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/ExerciseInfoCallback.kt
@@ -38,6 +38,6 @@
 
     @Throws(RemoteException::class)
     override fun onFailure(message: String) {
-        resultFuture.setException(Exception(message))
+        resultFuture.setException(RemoteException(message))
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt
index 874eec1..0d71261 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt
@@ -39,6 +39,6 @@
     @Throws(RemoteException::class)
     @CallSuper
     override fun onFailure(msg: String) {
-        resultFuture.setException(Exception(msg))
+        resultFuture.setException(RemoteException(msg))
     }
 }
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt
new file mode 100644
index 0000000..8d2e322
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt
@@ -0,0 +1,881 @@
+/*
+ * Copyright 2022 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.health.services.client
+
+import android.app.Application
+import android.content.ComponentName
+import android.content.Intent
+import android.os.Looper
+import android.os.RemoteException
+import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.ComparisonType
+import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DataTypeAvailability
+import androidx.health.services.client.data.DataTypeCondition
+import androidx.health.services.client.data.ExerciseCapabilities
+import androidx.health.services.client.data.ExerciseConfig
+import androidx.health.services.client.data.ExerciseGoal
+import androidx.health.services.client.data.ExerciseInfo
+import androidx.health.services.client.data.ExerciseLapSummary
+import androidx.health.services.client.data.ExerciseTrackedStatus
+import androidx.health.services.client.data.ExerciseType
+import androidx.health.services.client.data.ExerciseTypeCapabilities
+import androidx.health.services.client.data.ExerciseUpdate
+import androidx.health.services.client.data.WarmUpConfig
+import androidx.health.services.client.impl.IExerciseApiService
+import androidx.health.services.client.impl.IExerciseUpdateListener
+import androidx.health.services.client.impl.IpcConstants
+import androidx.health.services.client.impl.ServiceBackedExerciseClient
+import androidx.health.services.client.impl.event.ExerciseUpdateListenerEvent
+import androidx.health.services.client.impl.internal.IExerciseInfoCallback
+import androidx.health.services.client.impl.internal.IStatusCallback
+import androidx.health.services.client.impl.ipc.ClientConfiguration
+import androidx.health.services.client.impl.ipc.internal.ConnectionManager
+import androidx.health.services.client.impl.request.AutoPauseAndResumeConfigRequest
+import androidx.health.services.client.impl.request.CapabilitiesRequest
+import androidx.health.services.client.impl.request.ExerciseGoalRequest
+import androidx.health.services.client.impl.request.FlushRequest
+import androidx.health.services.client.impl.request.PrepareExerciseRequest
+import androidx.health.services.client.impl.request.StartExerciseRequest
+import androidx.health.services.client.impl.response.AvailabilityResponse
+import androidx.health.services.client.impl.response.ExerciseCapabilitiesResponse
+import androidx.health.services.client.impl.response.ExerciseInfoResponse
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.collect.ImmutableMap
+import com.google.common.collect.ImmutableSet
+import com.google.common.truth.Truth
+import java.util.concurrent.CancellationException
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows
+
+@RunWith(RobolectricTestRunner::class)
+class ExerciseClientTest {
+
+    private lateinit var client: ServiceBackedExerciseClient
+    private lateinit var service: FakeServiceStub
+    private val callback = FakeExerciseUpdateCallback()
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    private fun TestScope.advanceMainLooperIdle() =
+        launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        client =
+            ServiceBackedExerciseClient(context, ConnectionManager(context, context.mainLooper))
+        service = FakeServiceStub()
+
+        val packageName = CLIENT_CONFIGURATION.servicePackageName
+        val action = CLIENT_CONFIGURATION.bindAction
+        Shadows.shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
+            Intent().setPackage(packageName).setAction(action),
+            ComponentName(packageName, CLIENT),
+            service
+        )
+    }
+
+    @After
+    fun tearDown() {
+        client.clearUpdateCallbackAsync(callback)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_prepareExerciseSynchronously() = runTest {
+        launch {
+            val warmUpConfig = WarmUpConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+            )
+            val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+                AvailabilityResponse(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+            )
+            client.setUpdateCallback(callback)
+            client.prepareExercise(warmUpConfig)
+
+            service.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+            Shadows.shadowOf(Looper.getMainLooper()).idle()
+
+            Truth.assertThat(callback.availabilities)
+                .containsEntry(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_prepareExerciseSynchronously_ThrowsException() =
+        runTest {
+            launch {
+                val warmUpConfig = WarmUpConfig(
+                    ExerciseType.WALKING,
+                    setOf(DataType.HEART_RATE_BPM),
+                )
+                var exception: Exception? = null
+                client.setUpdateCallback(callback)
+                // Mocking the calling app already has an active exercise in
+                // progress or if it does not have the required permissions
+                service.throwException = true
+
+                try {
+                    client.prepareExercise(warmUpConfig)
+                } catch (e: RemoteException) {
+                    exception = e
+                }
+
+                Truth.assertThat(exception).isNotNull()
+                Truth.assertThat(exception).isInstanceOf(RemoteException::class.java)
+            }
+            advanceMainLooperIdle()
+        }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_startExerciseSynchronously() = runTest {
+        launch {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+                AvailabilityResponse(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+            )
+            client.setUpdateCallback(callback)
+            client.startExercise(exerciseConfig)
+
+            service.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+            Shadows.shadowOf(Looper.getMainLooper()).idle()
+
+            Truth.assertThat(callback.availabilities)
+                .containsEntry(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justStatsType_startExerciseSynchronously() = runTest {
+        launch {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM_STATS),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+                // Currently the proto form of HEART_RATE_BPM and HEART_RATE_BPM_STATS is identical.
+                // The APK doesn't know about _STATS, so pass the sample type to mimic that
+                // behavior.
+                AvailabilityResponse(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+            )
+            client.setUpdateCallback(callback)
+            client.startExercise(exerciseConfig)
+
+            service.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+            Shadows.shadowOf(Looper.getMainLooper()).idle()
+
+            Truth.assertThat(callback.availabilities)
+                .containsEntry(DataType.HEART_RATE_BPM_STATS, DataTypeAvailability.ACQUIRING)
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_statsAndSample_startExerciseSynchronously() = runTest {
+        launch {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM, DataType.HEART_RATE_BPM_STATS),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+                // Currently the proto form of HEART_RATE_BPM and HEART_RATE_BPM_STATS is identical.
+                // The APK doesn't know about _STATS, so pass the sample type to mimic that
+                // behavior.
+                AvailabilityResponse(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+            )
+            client.setUpdateCallback(callback)
+            client.startExercise(exerciseConfig)
+
+            service.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+            Shadows.shadowOf(Looper.getMainLooper()).idle()
+
+            // When both the sample type and stat type are requested, both should be notified
+            Truth.assertThat(callback.availabilities)
+                .containsEntry(DataType.HEART_RATE_BPM, DataTypeAvailability.ACQUIRING)
+            Truth.assertThat(callback.availabilities)
+                .containsEntry(DataType.HEART_RATE_BPM_STATS, DataTypeAvailability.ACQUIRING)
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_pauseExerciseSynchronously() = runTest {
+        val statesList = mutableListOf<TestExerciseStates>()
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val pauseExercise = async {
+
+            client.pauseExercise()
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        pauseExercise.await()
+
+        Truth.assertThat(statesList).containsExactly(
+            TestExerciseStates.STARTED,
+            TestExerciseStates.PAUSED
+        )
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_resumeExerciseSynchronously() = runTest {
+        val statesList = mutableListOf<TestExerciseStates>()
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val pauseExercise = async {
+
+            client.pauseExercise()
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        pauseExercise.await()
+        val resumeExercise = async {
+
+            client.resumeExercise()
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        resumeExercise.await()
+
+        Truth.assertThat(statesList).containsExactly(
+            TestExerciseStates.STARTED,
+            TestExerciseStates.PAUSED,
+            TestExerciseStates.RESUMED
+        )
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_endExerciseSynchronously() = runTest {
+        val statesList = mutableListOf<TestExerciseStates>()
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val endExercise = async {
+
+            client.endExercise()
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        endExercise.await()
+
+        Truth.assertThat(statesList).containsExactly(
+            TestExerciseStates.STARTED,
+            TestExerciseStates.ENDED
+        )
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun callbackShouldMatchRequested_justSampleType_endPausedExerciseSynchronously() = runTest {
+        val statesList = mutableListOf<TestExerciseStates>()
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val pauseExercise = async {
+
+            client.pauseExercise()
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        pauseExercise.await()
+        val endExercise = async {
+
+            client.endExercise()
+            statesList += service.testExerciseStates
+        }
+        advanceMainLooperIdle()
+        endExercise.await()
+
+        Truth.assertThat(statesList).containsExactly(
+            TestExerciseStates.STARTED,
+            TestExerciseStates.PAUSED,
+            TestExerciseStates.ENDED
+        )
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun flushSynchronously() = runTest {
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val flushExercise = async {
+
+            client.flush()
+        }
+        advanceMainLooperIdle()
+        flushExercise.await()
+
+        Truth.assertThat(service.registerFlushRequests).hasSize(1)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun markLapSynchronously() = runTest {
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val markLap = async {
+
+            client.markLap()
+        }
+        advanceMainLooperIdle()
+        markLap.await()
+
+        Truth.assertThat(service.laps).isEqualTo(1)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCurrentExerciseInfoSynchronously() = runTest {
+        lateinit var exerciseInfo: ExerciseInfo
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val currentExerciseInfoDeferred = async {
+            exerciseInfo = client.getCurrentExerciseInfo()
+        }
+        advanceMainLooperIdle()
+        currentExerciseInfoDeferred.await()
+
+        Truth.assertThat(exerciseInfo.exerciseType).isEqualTo(ExerciseType.WALKING)
+        Truth.assertThat(exerciseInfo.exerciseTrackedStatus)
+            .isEqualTo(ExerciseTrackedStatus.OWNED_EXERCISE_IN_PROGRESS)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCurrentExerciseInfoSynchronously_cancelled() = runTest {
+        var isCancellationException = false
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val currentExerciseInfoDeferred = async {
+            client.getCurrentExerciseInfo()
+        }
+        val cancelDeferred = async {
+            currentExerciseInfoDeferred.cancel()
+        }
+        try {
+            currentExerciseInfoDeferred.await()
+        } catch (e: CancellationException) {
+            isCancellationException = true
+        }
+        cancelDeferred.await()
+
+        Truth.assertThat(isCancellationException).isTrue()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCurrentExerciseInfoSynchronously_exception() = runTest {
+        var isException = false
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val currentExerciseInfoDeferred = async {
+            service.throwException = true
+            try {
+                client.getCurrentExerciseInfo()
+            } catch (e: RemoteException) {
+                isException = true
+            }
+        }
+        advanceMainLooperIdle()
+        currentExerciseInfoDeferred.await()
+
+        Truth.assertThat(isException).isTrue()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun clearUpdateCallbackShouldBeInvoked() = runTest {
+        val statesList = mutableListOf<Boolean>()
+
+        client.setUpdateCallback(callback)
+        Shadows.shadowOf(Looper.getMainLooper()).idle()
+        statesList += (service.listener == null)
+        val deferred = async {
+
+            client.clearUpdateCallback(callback)
+            statesList += (service.listener == null)
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(statesList).containsExactly(false, true)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun addGoalToActiveExerciseShouldBeInvoked() = runTest {
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false,
+                exerciseGoals = listOf(
+                    ExerciseGoal.createOneTimeGoal(
+                        DataTypeCondition(
+                            DataType.DISTANCE_TOTAL, 50.0,
+                            ComparisonType.GREATER_THAN
+                        )
+                    ),
+                    ExerciseGoal.createOneTimeGoal(
+                        DataTypeCondition(
+                            DataType.DISTANCE_TOTAL, 150.0,
+                            ComparisonType.GREATER_THAN
+                        )
+                    ),
+                )
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val addGoalDeferred = async {
+            val proto = ExerciseGoal.createOneTimeGoal(
+                DataTypeCondition(DataType.HEART_RATE_BPM_STATS, 145.0, ComparisonType.GREATER_THAN)
+            ).proto
+            val goal = ExerciseGoal.fromProto(proto)
+
+            client.addGoalToActiveExercise(goal)
+        }
+        advanceMainLooperIdle()
+        addGoalDeferred.await()
+
+        Truth.assertThat(service.goals).hasSize(3)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun removeGoalFromActiveExerciseShouldBeInvoked() = runTest {
+        val goal1 = ExerciseGoal.createOneTimeGoal(
+            DataTypeCondition(
+                DataType.DISTANCE_TOTAL, 50.0,
+                ComparisonType.GREATER_THAN
+            )
+        )
+        val goal2 = ExerciseGoal.createOneTimeGoal(
+            DataTypeCondition(
+                DataType.DISTANCE_TOTAL, 150.0,
+                ComparisonType.GREATER_THAN
+            )
+        )
+        val startExercise = async {
+            val exerciseConfig = ExerciseConfig(
+                ExerciseType.WALKING,
+                setOf(DataType.HEART_RATE_BPM),
+                isAutoPauseAndResumeEnabled = false,
+                isGpsEnabled = false,
+                exerciseGoals = listOf(
+                    goal1,
+                    goal2,
+                )
+            )
+            client.setUpdateCallback(callback)
+
+            client.startExercise(exerciseConfig)
+        }
+        advanceMainLooperIdle()
+        startExercise.await()
+        val removeGoalDeferred = async {
+            client.removeGoalFromActiveExercise(goal1)
+        }
+        advanceMainLooperIdle()
+        removeGoalDeferred.await()
+
+        Truth.assertThat(service.goals).hasSize(1)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCapabilitiesSynchronously() = runTest {
+        lateinit var passiveMonitoringCapabilities: ExerciseCapabilities
+        val deferred = async {
+            passiveMonitoringCapabilities = client.getCapabilities()
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(service.registerGetCapabilitiesRequests).hasSize(1)
+        Truth.assertThat(passiveMonitoringCapabilities).isNotNull()
+        Truth.assertThat(service.getTestCapabilities().toString())
+            .isEqualTo(passiveMonitoringCapabilities.toString())
+    }
+
+    class FakeExerciseUpdateCallback : ExerciseUpdateCallback {
+        val availabilities = mutableMapOf<DataType<*, *>, Availability>()
+        val registrationFailureThrowables = mutableListOf<Throwable>()
+        var >
+        var >
+        var update: ExerciseUpdate? = null
+
+        override fun onRegistered() {
+            onRegisteredCalls++
+        }
+
+        override fun onRegistrationFailed(throwable: Throwable) {
+            onRegistrationFailedCalls++
+            registrationFailureThrowables.add(throwable)
+        }
+
+        override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
+            this@FakeExerciseUpdateCallback.update = update
+        }
+
+        override fun onLapSummaryReceived(lapSummary: ExerciseLapSummary) {}
+
+        override fun onAvailabilityChanged(dataType: DataType<*, *>, availability: Availability) {
+            availabilities[dataType] = availability
+        }
+    }
+
+    class FakeServiceStub : IExerciseApiService.Stub() {
+
+        var listener: IExerciseUpdateListener? = null
+        val registerFlushRequests = mutableListOf<FlushRequest>()
+        var statusCallbackAction: (IStatusCallback?) -> Unit = { it!!.onSuccess() }
+        var testExerciseStates = TestExerciseStates.UNKNOWN
+        var laps = 0
+        var exerciseConfig: ExerciseConfig? = null
+        override fun getApiVersion(): Int = 12
+        val goals = mutableListOf<ExerciseGoal<*>>()
+        var throwException = false
+        val registerGetCapabilitiesRequests = mutableListOf<CapabilitiesRequest>()
+
+        override fun prepareExercise(
+            prepareExerciseRequest: PrepareExerciseRequest?,
+            statusCallback: IStatusCallback
+        ) {
+            if (throwException) {
+                statusCallback.onFailure("Remote Exception")
+            } else {
+                statusCallbackAction.invoke(statusCallback)
+            }
+        }
+
+        override fun startExercise(
+            startExerciseRequest: StartExerciseRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            exerciseConfig = startExerciseRequest?.exerciseConfig
+            exerciseConfig?.exerciseGoals?.let { goals.addAll(it) }
+            statusCallbackAction.invoke(statusCallback)
+            testExerciseStates = TestExerciseStates.STARTED
+        }
+
+        override fun pauseExercise(packageName: String?, statusCallback: IStatusCallback?) {
+            statusCallbackAction.invoke(statusCallback)
+            testExerciseStates = TestExerciseStates.PAUSED
+        }
+
+        override fun resumeExercise(packageName: String?, statusCallback: IStatusCallback?) {
+            statusCallbackAction.invoke(statusCallback)
+            testExerciseStates = TestExerciseStates.RESUMED
+        }
+
+        override fun endExercise(packageName: String?, statusCallback: IStatusCallback?) {
+            statusCallbackAction.invoke(statusCallback)
+            testExerciseStates = TestExerciseStates.ENDED
+        }
+
+        override fun markLap(packageName: String?, statusCallback: IStatusCallback?) {
+            laps++
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun getCurrentExerciseInfo(
+            packageName: String?,
+            exerciseInfoCallback: IExerciseInfoCallback?
+        ) {
+            if (throwException) {
+                exerciseInfoCallback?.onFailure("Remote Exception")
+            }
+            if (exerciseConfig == null) {
+                exerciseInfoCallback?.onExerciseInfo(
+                    ExerciseInfoResponse(
+                        ExerciseInfo(
+                            ExerciseTrackedStatus.UNKNOWN,
+                            ExerciseType.UNKNOWN
+                        )
+                    )
+                )
+            } else {
+                exerciseInfoCallback?.onExerciseInfo(
+                    ExerciseInfoResponse(
+                        ExerciseInfo(
+                            ExerciseTrackedStatus.OWNED_EXERCISE_IN_PROGRESS,
+                            exerciseConfig!!.exerciseType
+                        )
+                    )
+                )
+            }
+        }
+
+        override fun setUpdateListener(
+            packageName: String?,
+            listener: IExerciseUpdateListener?,
+            statusCallback: IStatusCallback?
+        ) {
+            this.listener = listener
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun clearUpdateListener(
+            packageName: String?,
+            listener: IExerciseUpdateListener?,
+            statusCallback: IStatusCallback?
+        ) {
+            if (this.listener == listener)
+                this.listener = null
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun addGoalToActiveExercise(
+            request: ExerciseGoalRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            if (request != null) {
+                goals.add(request.exerciseGoal)
+            }
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun removeGoalFromActiveExercise(
+            request: ExerciseGoalRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            if (request != null) {
+                goals.remove(request.exerciseGoal)
+            }
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun overrideAutoPauseAndResumeForActiveExercise(
+            request: AutoPauseAndResumeConfigRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            throw NotImplementedError()
+        }
+
+        override fun getCapabilities(request: CapabilitiesRequest): ExerciseCapabilitiesResponse {
+            if (throwException) {
+                throw RemoteException("Remote Exception")
+            }
+            registerGetCapabilitiesRequests.add(request)
+            val capabilities = getTestCapabilities()
+            return ExerciseCapabilitiesResponse(capabilities)
+        }
+
+        fun getTestCapabilities(): ExerciseCapabilities {
+            val exerciseTypeToCapabilitiesMapping =
+                ImmutableMap.of(
+                    ExerciseType.WALKING, ExerciseTypeCapabilities( /* supportedDataTypes= */
+                        ImmutableSet.of(DataType.STEPS),
+                        ImmutableMap.of(
+                            DataType.STEPS_TOTAL,
+                            ImmutableSet.of(ComparisonType.GREATER_THAN)
+                        ),
+                        ImmutableMap.of(
+                            DataType.STEPS_TOTAL,
+                            ImmutableSet.of(ComparisonType.LESS_THAN, ComparisonType.GREATER_THAN)
+                        ), /* supportsAutoPauseAndResume= */
+                        false
+                    ),
+                    ExerciseType.RUNNING, ExerciseTypeCapabilities(
+                        ImmutableSet.of(DataType.HEART_RATE_BPM, DataType.SPEED),
+                        ImmutableMap.of(
+                            DataType.HEART_RATE_BPM_STATS,
+                            ImmutableSet.of(ComparisonType.GREATER_THAN, ComparisonType.LESS_THAN),
+                            DataType.SPEED_STATS,
+                            ImmutableSet.of(ComparisonType.LESS_THAN)
+                        ),
+                        ImmutableMap.of(
+                            DataType.HEART_RATE_BPM_STATS,
+                            ImmutableSet.of(ComparisonType.GREATER_THAN_OR_EQUAL),
+                            DataType.SPEED_STATS,
+                            ImmutableSet.of(ComparisonType.LESS_THAN, ComparisonType.GREATER_THAN)
+                        ), /* supportsAutoPauseAndResume= */
+                        true
+                    ),
+                    ExerciseType.SWIMMING_POOL, ExerciseTypeCapabilities( /* supportedDataTypes= */
+                        ImmutableSet.of(), /* supportedGoals= */
+                        ImmutableMap.of(), /* supportedMilestones= */
+                        ImmutableMap.of(), /* supportsAutoPauseAndResume= */
+                        true
+                    )
+                )
+
+            return ExerciseCapabilities(exerciseTypeToCapabilitiesMapping)
+        }
+
+        override fun flushExercise(request: FlushRequest, statusCallback: IStatusCallback?) {
+            registerFlushRequests += request
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        fun setException() {
+            throwException = true
+        }
+    }
+
+    enum class TestExerciseStates {
+        UNKNOWN,
+        PREPARED,
+        STARTED,
+        PAUSED,
+        RESUMED,
+        ENDED
+    }
+
+    internal companion object {
+        internal const val CLIENT = "HealthServicesExerciseClient"
+        internal val CLIENT_CONFIGURATION =
+            ClientConfiguration(
+                CLIENT,
+                IpcConstants.SERVICE_PACKAGE_NAME,
+                IpcConstants.EXERCISE_API_BIND_ACTION
+            )
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/MeasureClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/MeasureClientTest.kt
new file mode 100644
index 0000000..b15bbca
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/MeasureClientTest.kt
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2022 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.health.services.client
+
+import android.app.Application
+import android.content.ComponentName
+import android.content.Intent
+import android.os.Looper
+import android.os.RemoteException
+import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.DataPointContainer
+import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DeltaDataType
+import androidx.health.services.client.data.MeasureCapabilities
+import androidx.health.services.client.impl.IMeasureApiService
+import androidx.health.services.client.impl.IMeasureCallback
+import androidx.health.services.client.impl.IpcConstants
+import androidx.health.services.client.impl.ServiceBackedMeasureClient
+import androidx.health.services.client.impl.internal.IStatusCallback
+import androidx.health.services.client.impl.ipc.ClientConfiguration
+import androidx.health.services.client.impl.ipc.internal.ConnectionManager
+import androidx.health.services.client.impl.request.CapabilitiesRequest
+import androidx.health.services.client.impl.request.MeasureRegistrationRequest
+import androidx.health.services.client.impl.request.MeasureUnregistrationRequest
+import androidx.health.services.client.impl.response.MeasureCapabilitiesResponse
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth
+import java.util.concurrent.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows
+
+@RunWith(RobolectricTestRunner::class)
+class MeasureClientTest {
+
+    private lateinit var callback: FakeCallback
+    private lateinit var client: ServiceBackedMeasureClient
+    private lateinit var service: FakeServiceStub
+    private var cleanup: Boolean = false
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    private fun TestScope.advanceMainLooperIdle() =
+        launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
+    private fun CoroutineScope.advanceMainLooperIdle() =
+        launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        callback = FakeCallback()
+        client =
+            ServiceBackedMeasureClient(context, ConnectionManager(context, context.mainLooper))
+        service = FakeServiceStub()
+
+        val packageName = CLIENT_CONFIGURATION.servicePackageName
+        val action = CLIENT_CONFIGURATION.bindAction
+        Shadows.shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
+            Intent().setPackage(packageName).setAction(action),
+            ComponentName(packageName, CLIENT),
+            service
+        )
+        cleanup = true
+    }
+
+    @After
+    fun tearDown() {
+        if (!cleanup)
+            return
+        runBlocking {
+            launch { client.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback) }
+            advanceMainLooperIdle()
+        }
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun unregisterCallbackReachesServiceSynchronously() = runTest {
+        val deferred = async {
+            client.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
+            client.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback)
+            cleanup = false // Already unregistered
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(service.unregisterEvents).hasSize(1)
+        Truth.assertThat(service.unregisterEvents[0].request.dataType)
+            .isEqualTo(DataType.HEART_RATE_BPM)
+        Truth.assertThat(service.unregisterEvents[0].request.packageName)
+            .isEqualTo("androidx.health.services.client.test")
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun unregisterCallbackSynchronously_throwsIllegalArgumentException() = runTest {
+        var isExceptionCaught = false
+
+        val deferred = async {
+            try {
+                client.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback)
+            } catch (e: IllegalArgumentException) {
+                isExceptionCaught = true
+            }
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+        cleanup = false // Not registered
+
+        Truth.assertThat(isExceptionCaught).isTrue()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun capabilitiesReturnsCorrectValueSynchronously() = runTest {
+        lateinit var capabilities: MeasureCapabilities
+        val deferred = async {
+            service.supportedDataTypes = setOf(DataType.HEART_RATE_BPM)
+            client.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
+            capabilities = client.getCapabilities()
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(capabilities.supportedDataTypesMeasure).hasSize(1)
+        Truth.assertThat(capabilities.supportedDataTypesMeasure)
+            .containsExactly(DataType.HEART_RATE_BPM)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun capabilitiesReturnsCorrectValue_cancelSuccessfully() = runTest {
+        var isCancellationException = false
+        val deferred = async {
+            service.supportedDataTypes = setOf(DataType.HEART_RATE_BPM)
+            client.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
+            client.getCapabilities()
+        }
+        val cancelCoroutine = async { deferred.cancel() }
+        try {
+            deferred.await()
+        } catch (e: CancellationException) {
+            isCancellationException = true
+        }
+        cancelCoroutine.await()
+
+        Truth.assertThat(isCancellationException).isTrue()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun capabilitiesReturnsCorrectValue_catchException() = runTest {
+        var isRemoteException = false
+        val deferred = async {
+            service.supportedDataTypes = setOf(DataType.HEART_RATE_BPM)
+            client.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
+            service.setException()
+            try {
+                client.getCapabilities()
+            } catch (e: RemoteException) {
+                isRemoteException = true
+            }
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(isRemoteException).isTrue()
+    }
+
+    class FakeCallback : MeasureCallback {
+        data class AvailabilityChangeEvent(
+            val dataType: DataType<*, *>,
+            val availability: Availability
+        )
+
+        data class DataReceivedEvent(val data: DataPointContainer)
+
+        val availabilityChangeEvents = mutableListOf<AvailabilityChangeEvent>()
+        val dataReceivedEvents = mutableListOf<DataReceivedEvent>()
+        var >
+        var registrationFailureThrowables = mutableListOf<Throwable>()
+
+        override fun onRegistered() {
+            onRegisteredInvocationCount++
+        }
+
+        override fun onRegistrationFailed(throwable: Throwable) {
+            registrationFailureThrowables += throwable
+        }
+
+        override fun onAvailabilityChanged(
+            dataType: DeltaDataType<*, *>,
+            availability: Availability
+        ) {
+            availabilityChangeEvents += AvailabilityChangeEvent(dataType, availability)
+        }
+
+        override fun onDataReceived(data: DataPointContainer) {
+            dataReceivedEvents += DataReceivedEvent(data)
+        }
+    }
+
+    class FakeServiceStub : IMeasureApiService.Stub() {
+        private var throwExcepotion = false
+
+        class RegisterEvent(
+            val request: MeasureRegistrationRequest,
+            val callback: IMeasureCallback,
+            val statusCallback: IStatusCallback
+        )
+
+        class UnregisterEvent(
+            val request: MeasureUnregistrationRequest,
+            val callback: IMeasureCallback,
+            val statusCallback: IStatusCallback
+        )
+
+        var statusCallbackAction: (IStatusCallback) -> Unit = { it.onSuccess() }
+        var supportedDataTypes = setOf(DataType.HEART_RATE_BPM)
+
+        val registerEvents = mutableListOf<RegisterEvent>()
+        val unregisterEvents = mutableListOf<UnregisterEvent>()
+
+        override fun getApiVersion() = 42
+
+        override fun registerCallback(
+            request: MeasureRegistrationRequest,
+            callback: IMeasureCallback,
+            statusCallback: IStatusCallback
+        ) {
+            registerEvents += RegisterEvent(request, callback, statusCallback)
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun unregisterCallback(
+            request: MeasureUnregistrationRequest,
+            callback: IMeasureCallback,
+            statusCallback: IStatusCallback
+        ) {
+            unregisterEvents += UnregisterEvent(request, callback, statusCallback)
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun getCapabilities(request: CapabilitiesRequest): MeasureCapabilitiesResponse {
+            if (throwExcepotion) {
+                throw RemoteException("Remote Exception")
+            }
+            return MeasureCapabilitiesResponse(MeasureCapabilities(supportedDataTypes))
+        }
+
+        fun setException() {
+            throwExcepotion = true
+        }
+    }
+
+    internal companion object {
+        internal const val CLIENT = "HealthServicesMeasureClient"
+        internal val CLIENT_CONFIGURATION =
+            ClientConfiguration(
+                CLIENT,
+                IpcConstants.SERVICE_PACKAGE_NAME,
+                IpcConstants.MEASURE_API_BIND_ACTION
+            )
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/PassiveMonitoringClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/PassiveMonitoringClientTest.kt
new file mode 100644
index 0000000..1a4a39d
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/PassiveMonitoringClientTest.kt
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2022 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.health.services.client
+
+import android.app.Application
+import android.content.ComponentName
+import android.content.Intent
+import android.os.Looper
+import android.os.RemoteException
+import androidx.health.services.client.data.DataPointContainer
+import androidx.health.services.client.data.DataType.Companion.CALORIES_DAILY
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import androidx.health.services.client.data.DataType.Companion.DISTANCE
+import androidx.health.services.client.data.DataType.Companion.STEPS
+import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
+import androidx.health.services.client.data.HealthEvent
+import androidx.health.services.client.data.PassiveGoal
+import androidx.health.services.client.data.PassiveListenerConfig
+import androidx.health.services.client.data.PassiveMonitoringCapabilities
+import androidx.health.services.client.data.UserActivityInfo
+import androidx.health.services.client.data.UserActivityState
+import androidx.health.services.client.impl.IPassiveListenerCallback
+import androidx.health.services.client.impl.IPassiveMonitoringApiService
+import androidx.health.services.client.impl.ServiceBackedPassiveMonitoringClient
+import androidx.health.services.client.impl.internal.IStatusCallback
+import androidx.health.services.client.impl.ipc.internal.ConnectionManager
+import androidx.health.services.client.impl.request.CapabilitiesRequest
+import androidx.health.services.client.impl.request.FlushRequest
+import androidx.health.services.client.impl.request.PassiveListenerCallbackRegistrationRequest
+import androidx.health.services.client.impl.request.PassiveListenerServiceRegistrationRequest
+import androidx.health.services.client.impl.response.PassiveMonitoringCapabilitiesResponse
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth
+import java.util.concurrent.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows
+
+@RunWith(RobolectricTestRunner::class)
+class PassiveMonitoringClientTest {
+
+    private lateinit var client: PassiveMonitoringClient
+    private lateinit var service: FakeServiceStub
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    private fun TestScope.advanceMainLooperIdle() =
+        launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
+
+    private fun CoroutineScope.advanceMainLooperIdle() =
+        launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        client = ServiceBackedPassiveMonitoringClient(
+            context, ConnectionManager(context, context.mainLooper)
+        )
+        service = FakeServiceStub()
+
+        val packageName =
+            ServiceBackedPassiveMonitoringClient.CLIENT_CONFIGURATION.servicePackageName
+        val action = ServiceBackedPassiveMonitoringClient.CLIENT_CONFIGURATION.bindAction
+        Shadows.shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
+            Intent().setPackage(packageName).setAction(action),
+            ComponentName(packageName, ServiceBackedPassiveMonitoringClient.CLIENT),
+            service
+        )
+    }
+
+    @After
+    fun tearDown() {
+        runBlocking {
+            launch { client.clearPassiveListenerCallback() }
+            advanceMainLooperIdle()
+            launch { client.clearPassiveListenerService() }
+            advanceMainLooperIdle()
+        }
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun registersPassiveListenerServiceSynchronously() = runTest {
+        launch {
+            val config = PassiveListenerConfig(
+                dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
+                shouldUserActivityInfoBeRequested = true,
+                dailyGoals = setOf(),
+                healthEventTypes = setOf()
+            )
+
+            client.setPassiveListenerService(FakeListenerService::class.java, config)
+            val request = service.registerServiceRequests[0]
+
+            Truth.assertThat(service.registerServiceRequests).hasSize(1)
+            Truth.assertThat(request.passiveListenerConfig.dataTypes).containsExactly(
+                STEPS_DAILY, CALORIES_DAILY
+            )
+            Truth.assertThat(request.passiveListenerConfig.shouldUserActivityInfoBeRequested)
+                .isTrue()
+            Truth.assertThat(request.packageName).isEqualTo("androidx.health.services.client.test")
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun flushSynchronously() = runTest {
+        launch {
+            val config = PassiveListenerConfig(
+                dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
+                shouldUserActivityInfoBeRequested = true,
+                dailyGoals = setOf(),
+                healthEventTypes = setOf()
+            )
+            val callback = FakeCallback()
+            client.setPassiveListenerCallback(config, callback)
+
+            client.flush()
+
+            Truth.assertThat(service.registerFlushRequests).hasSize(1)
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCapabilitiesSynchronously() = runTest {
+        launch {
+            val config = PassiveListenerConfig(
+                dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
+                shouldUserActivityInfoBeRequested = true,
+                dailyGoals = setOf(),
+                healthEventTypes = setOf()
+            )
+            val callback = FakeCallback()
+            client.setPassiveListenerCallback(config, callback)
+
+            val passiveMonitoringCapabilities = client.getCapabilities()
+
+            Truth.assertThat(service.registerGetCapabilitiesRequests).hasSize(1)
+            Truth.assertThat(passiveMonitoringCapabilities).isNotNull()
+            Truth.assertThat(service.getTestCapabilities().toString())
+                .isEqualTo(passiveMonitoringCapabilities.toString())
+        }
+        advanceMainLooperIdle()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCapabilitiesSynchronously_cancelled() = runTest {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
+            shouldUserActivityInfoBeRequested = true,
+            dailyGoals = setOf(),
+            healthEventTypes = setOf()
+        )
+        val callback = FakeCallback()
+        client.setPassiveListenerCallback(config, callback)
+        var isCancellationException = false
+
+        val deferred = async {
+            client.getCapabilities()
+        }
+        val cancellationDeferred = async {
+            deferred.cancel(CancellationException())
+        }
+        try {
+            deferred.await()
+        } catch (e: CancellationException) {
+            isCancellationException = true
+        }
+        cancellationDeferred.await()
+
+        Truth.assertThat(isCancellationException).isTrue()
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun getCapabilitiesSynchronously_Exception() = runTest {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
+            shouldUserActivityInfoBeRequested = true,
+            dailyGoals = setOf(),
+            healthEventTypes = setOf()
+        )
+        val callback = FakeCallback()
+        client.setPassiveListenerCallback(config, callback)
+        var isExceptionCaught = false
+        val deferred = async {
+            service.setException()
+            try {
+
+                client.getCapabilities()
+            } catch (e: RemoteException) {
+                isExceptionCaught = true
+            }
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(isExceptionCaught).isTrue()
+    }
+
+    class FakeListenerService : PassiveListenerService()
+
+    internal class FakeCallback : PassiveListenerCallback {
+        private var >
+        private val >
+        private val dataPointsReceived = mutableListOf<DataPointContainer>()
+        private val userActivityInfosReceived = mutableListOf<UserActivityInfo>()
+        private val completedGoals = mutableListOf<PassiveGoal>()
+        private val healthEventsReceived = mutableListOf<HealthEvent>()
+        var >
+
+        override fun onRegistered() {
+            onRegisteredCalls++
+        }
+
+        override fun onRegistrationFailed(throwable: Throwable) {
+            onRegistrationFailedThrowables += throwable
+        }
+
+        override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
+            dataPointsReceived += dataPoints
+        }
+
+        override fun onUserActivityInfoReceived(info: UserActivityInfo) {
+            userActivityInfosReceived += info
+        }
+
+        override fun onGoalCompleted(goal: PassiveGoal) {
+            completedGoals += goal
+        }
+
+        override fun onHealthEventReceived(event: HealthEvent) {
+            healthEventsReceived += event
+        }
+
+        override fun onPermissionLost() {
+            onPermissionLostCalls++
+        }
+    }
+
+    internal class FakeServiceStub : IPassiveMonitoringApiService.Stub() {
+        @JvmField
+        var apiVersion = 42
+
+        private var statusCallbackAction: (IStatusCallback?) -> Unit = { it!!.onSuccess() }
+        val registerServiceRequests = mutableListOf<PassiveListenerServiceRegistrationRequest>()
+        private val registerCallbackRequests =
+            mutableListOf<PassiveListenerCallbackRegistrationRequest>()
+        val registerFlushRequests = mutableListOf<FlushRequest>()
+        val registerGetCapabilitiesRequests = mutableListOf<CapabilitiesRequest>()
+        private val registeredCallbacks = mutableListOf<IPassiveListenerCallback>()
+        private val unregisterServicePackageNames = mutableListOf<String>()
+        private val unregisterCallbackPackageNames = mutableListOf<String>()
+        private var throwExcepotion = false
+
+        override fun getApiVersion() = 42
+
+        override fun getCapabilities(
+            request: CapabilitiesRequest
+        ): PassiveMonitoringCapabilitiesResponse {
+            if (throwExcepotion) {
+                throw RemoteException("Remote Exception")
+            }
+            registerGetCapabilitiesRequests.add(request)
+            val capabilities = getTestCapabilities()
+            return PassiveMonitoringCapabilitiesResponse(capabilities)
+        }
+
+        override fun flush(request: FlushRequest, statusCallback: IStatusCallback?) {
+            registerFlushRequests.add(request)
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun registerPassiveListenerService(
+            request: PassiveListenerServiceRegistrationRequest,
+            statusCallback: IStatusCallback
+        ) {
+            registerServiceRequests += request
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun registerPassiveListenerCallback(
+            request: PassiveListenerCallbackRegistrationRequest,
+            callback: IPassiveListenerCallback,
+            statusCallback: IStatusCallback
+        ) {
+            registerCallbackRequests += request
+            registeredCallbacks += callback
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun unregisterPassiveListenerService(
+            packageName: String,
+            statusCallback: IStatusCallback
+        ) {
+            unregisterServicePackageNames += packageName
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun unregisterPassiveListenerCallback(
+            packageName: String,
+            statusCallback: IStatusCallback
+        ) {
+            unregisterCallbackPackageNames += packageName
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        fun getTestCapabilities(): PassiveMonitoringCapabilities {
+            return PassiveMonitoringCapabilities(
+                supportedDataTypesPassiveMonitoring = setOf(STEPS, DISTANCE),
+                supportedDataTypesPassiveGoals = setOf(CALORIES_TOTAL),
+                supportedHealthEventTypes = setOf(HealthEvent.Type.FALL_DETECTED),
+                supportedUserActivityStates = setOf(UserActivityState.USER_ACTIVITY_PASSIVE)
+            )
+        }
+
+        fun setException() {
+            throwExcepotion = true
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt
index a86c098..f42c82c 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt
@@ -76,6 +76,7 @@
     @After
     fun tearDown() {
         client.unregisterMeasureCallbackAsync(HEART_RATE_BPM, callback)
+        shadowOf(Looper.getMainLooper()).idle()
     }
 
     @Test
diff --git a/input/input-motionprediction/api/current.txt b/input/input-motionprediction/api/current.txt
index a2cdf99..6611119 100644
--- a/input/input-motionprediction/api/current.txt
+++ b/input/input-motionprediction/api/current.txt
@@ -1,11 +1,11 @@
 // Signature format: 4.0
 package androidx.input.motionprediction {
 
-  public interface MotionEventPredictor {
-    method public void dispose();
+  public interface MotionEventPredictor extends java.lang.AutoCloseable {
+    method public void close();
     method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
     method public android.view.MotionEvent? predict();
-    method public void recordMovement(android.view.MotionEvent);
+    method public void record(android.view.MotionEvent);
   }
 
 }
diff --git a/input/input-motionprediction/api/public_plus_experimental_current.txt b/input/input-motionprediction/api/public_plus_experimental_current.txt
index a2cdf99..6611119 100644
--- a/input/input-motionprediction/api/public_plus_experimental_current.txt
+++ b/input/input-motionprediction/api/public_plus_experimental_current.txt
@@ -1,11 +1,11 @@
 // Signature format: 4.0
 package androidx.input.motionprediction {
 
-  public interface MotionEventPredictor {
-    method public void dispose();
+  public interface MotionEventPredictor extends java.lang.AutoCloseable {
+    method public void close();
     method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
     method public android.view.MotionEvent? predict();
-    method public void recordMovement(android.view.MotionEvent);
+    method public void record(android.view.MotionEvent);
   }
 
 }
diff --git a/input/input-motionprediction/api/restricted_current.txt b/input/input-motionprediction/api/restricted_current.txt
index a2cdf99..6611119 100644
--- a/input/input-motionprediction/api/restricted_current.txt
+++ b/input/input-motionprediction/api/restricted_current.txt
@@ -1,11 +1,11 @@
 // Signature format: 4.0
 package androidx.input.motionprediction {
 
-  public interface MotionEventPredictor {
-    method public void dispose();
+  public interface MotionEventPredictor extends java.lang.AutoCloseable {
+    method public void close();
     method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
     method public android.view.MotionEvent? predict();
-    method public void recordMovement(android.view.MotionEvent);
+    method public void record(android.view.MotionEvent);
   }
 
 }
diff --git a/input/input-motionprediction/build.gradle b/input/input-motionprediction/build.gradle
index 9475cdb..8d3d5e2 100644
--- a/input/input-motionprediction/build.gradle
+++ b/input/input-motionprediction/build.gradle
@@ -33,7 +33,7 @@
 
 android {
     defaultConfig {
-        minSdkVersion 16
+        minSdkVersion 19
     }
     namespace "androidx.input.motionprediction"
 }
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java
index c27d293..31442ee 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java
@@ -33,7 +33,7 @@
  * rendered on the display. Once no more predictions are needed, call {@link #dispose()} to stop it
  * and clean up resources.
  */
-public interface MotionEventPredictor {
+public interface MotionEventPredictor extends AutoCloseable {
     /**
      * Record a user's movement to the predictor. You should call this for every
      * {@link android.view.MotionEvent} that is received by the associated
@@ -41,7 +41,7 @@
      * @param event the {@link android.view.MotionEvent} the associated view received and that
      *              needs to be recorded.
      */
-    void recordMovement(@NonNull MotionEvent event);
+    void record(@NonNull MotionEvent event);
 
     /**
      * Compute a prediction
@@ -55,7 +55,8 @@
      * Notify the predictor that no more predictions are needed. Any subsequent call to
      * {@link #predict()} will return null.
      */
-    void dispose();
+    @Override
+    void close();
 
     /**
      * Create a new motion predictor associated to a specific {@link android.view.View}
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java
index e5abbd9..df88594 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java
@@ -31,7 +31,7 @@
 @RestrictTo(LIBRARY)
 public class KalmanMotionEventPredictor implements MotionEventPredictor {
     private MultiPointerPredictor mMultiPointerPredictor;
-    private boolean mDisposed = false;
+    private boolean mClosed = false;
 
     public KalmanMotionEventPredictor() {
         mMultiPointerPredictor = new MultiPointerPredictor();
@@ -43,21 +43,21 @@
     }
 
     @Override
-    public void recordMovement(@NonNull MotionEvent event) {
+    public void record(@NonNull MotionEvent event) {
         mMultiPointerPredictor.onTouchEvent(event);
     }
 
     @Nullable
     @Override
     public MotionEvent predict() {
-        if (mDisposed) {
+        if (mClosed) {
             return null;
         }
         return mMultiPointerPredictor.predict();
     }
 
     @Override
-    public void dispose() {
-        mDisposed = true;
+    public void close() {
+        mClosed = true;
     }
 }
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/InkPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanPredictor.java
similarity index 97%
rename from input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/InkPredictor.java
rename to input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanPredictor.java
index 98f4a44..f655aec 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/InkPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanPredictor.java
@@ -30,7 +30,7 @@
  * @hide
  */
 @RestrictTo(LIBRARY)
-public interface InkPredictor {
+public interface KalmanPredictor {
 
     /** Gets the current prediction target */
     int getPredictionTarget();
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java
index c97dd19..1b0b3cd 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java
@@ -32,11 +32,11 @@
  * @hide
  */
 @RestrictTo(LIBRARY)
-public class MultiPointerPredictor implements InkPredictor {
+public class MultiPointerPredictor implements KalmanPredictor {
     private static final String TAG = "MultiPointerPredictor";
     private static final boolean DEBUG_PREDICTION = Log.isLoggable(TAG, Log.DEBUG);
 
-    private SparseArray<KalmanInkPredictor> mPredictorMap = new SparseArray<>();
+    private SparseArray<SinglePointerPredictor> mPredictorMap = new SparseArray<>();
     private int mPredictionTargetMs = 0;
     private int mReportRateMs = 0;
 
@@ -77,7 +77,7 @@
         int action = event.getActionMasked();
         int pointerId = event.getPointerId(event.getActionIndex());
         if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {
-            KalmanInkPredictor predictor = new KalmanInkPredictor();
+            SinglePointerPredictor predictor = new SinglePointerPredictor();
             predictor.setPredictionTarget(mPredictionTargetMs);
             if (mReportRateMs > 0) {
                 predictor.setReportRate(mReportRateMs);
@@ -86,14 +86,14 @@
             predictor.onTouchEvent(event);
             mPredictorMap.put(pointerId, predictor);
         } else if (action == MotionEvent.ACTION_UP) {
-            KalmanInkPredictor predictor = mPredictorMap.get(pointerId);
+            SinglePointerPredictor predictor = mPredictorMap.get(pointerId);
             if (predictor != null) {
                 mPredictorMap.remove(pointerId);
                 predictor.onTouchEvent(event);
             }
             mPredictorMap.clear();
         } else if (action == MotionEvent.ACTION_POINTER_UP) {
-            KalmanInkPredictor predictor = mPredictorMap.get(pointerId);
+            SinglePointerPredictor predictor = mPredictorMap.get(pointerId);
             if (predictor != null) {
                 mPredictorMap.remove(pointerId);
                 predictor.onTouchEvent(event);
@@ -126,7 +126,7 @@
             return null;
         }
         if (pointerCount == 1) {
-            KalmanInkPredictor predictor = mPredictorMap.valueAt(0);
+            SinglePointerPredictor predictor = mPredictorMap.valueAt(0);
             MotionEvent predictedEv = predictor.predict();
             if (DEBUG_PREDICTION) {
                 Log.d(TAG, "predict() -> MotionEvent: " + predictedEv);
@@ -139,7 +139,7 @@
         MotionEvent[] singlePointerEvents = new MotionEvent[pointerCount];
         for (int i = 0; i < pointerCount; ++i) {
             pointerIds[i] = mPredictorMap.keyAt(i);
-            KalmanInkPredictor predictor = mPredictorMap.valueAt(i);
+            SinglePointerPredictor predictor = mPredictorMap.valueAt(i);
             singlePointerEvents[i] = predictor.predict();
             // If predictor consumer expect more sample, generate sample where position and
             // pressure are constant
@@ -161,7 +161,9 @@
 
         if (foundNullPrediction) {
             for (MotionEvent ev : singlePointerEvents) {
-                ev.recycle();
+                if (ev != null) {
+                    ev.recycle();
+                }
             }
             return null;
         }
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PenKalmanFilter.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PointerKalmanFilter.java
similarity index 97%
rename from input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PenKalmanFilter.java
rename to input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PointerKalmanFilter.java
index a70b21e..6a80269 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PenKalmanFilter.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PointerKalmanFilter.java
@@ -29,7 +29,7 @@
  * @hide
  */
 @RestrictTo(LIBRARY)
-public class PenKalmanFilter {
+public class PointerKalmanFilter {
     private KalmanFilter mXKalman;
     private KalmanFilter mYKalman;
     private KalmanFilter mPKalman;
@@ -54,7 +54,7 @@
      * @param sigmaProcess lower value = more filtering
      * @param sigmaMeasurement higher value = more filtering
      */
-    public PenKalmanFilter(double sigmaProcess, double sigmaMeasurement) {
+    public PointerKalmanFilter(double sigmaProcess, double sigmaMeasurement) {
         mSigmaProcess = sigmaProcess;
         mSigmaMeasurement = sigmaMeasurement;
         mXKalman = createAxisKalmanFilter();
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanInkPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
similarity index 98%
rename from input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanInkPredictor.java
rename to input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
index 73c1f4a..0656734 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanInkPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
@@ -34,7 +34,7 @@
  * @hide
  */
 @RestrictTo(LIBRARY)
-public class KalmanInkPredictor implements InkPredictor {
+public class SinglePointerPredictor implements KalmanPredictor {
     private static final String TAG = "KalmanInkPredictor";
 
     // Influence of jank during each prediction sample
@@ -67,7 +67,7 @@
     // The Kalman filter is tuned to smooth noise while maintaining fast reaction to direction
     // changes. The stronger the filter, the smoother the prediction result will be, at the
     // cost of possible prediction errors.
-    private final PenKalmanFilter mKalman = new PenKalmanFilter(0.01, 1.0);
+    private final PointerKalmanFilter mKalman = new PointerKalmanFilter(0.01, 1.0);
 
     private final DVector2 mLastPosition = new DVector2();
     private long mPrevEventTime;
@@ -93,7 +93,7 @@
      * achieving close-to-zero latency, prediction errors can be more visible and the target should
      * be reduced to 20ms.
      */
-    public KalmanInkPredictor() {
+    public SinglePointerPredictor() {
         mKalman.reset();
         mPrevEventTime = 0;
     }
diff --git a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
index 83c849d..d5a45dc 100644
--- a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
+++ b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
@@ -24,6 +24,7 @@
 import androidx.navigation.NavHostController
 import androidx.navigation.createGraph
 import androidx.navigation.plusAssign
+import androidx.navigation.ui.test.R
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
diff --git a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt
index 0796ff3..992ee49 100644
--- a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt
+++ b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt
@@ -26,6 +26,7 @@
 import androidx.navigation.NavHostController
 import androidx.navigation.NavType
 import androidx.navigation.createGraph
+import androidx.navigation.ui.test.R
 import androidx.test.annotation.UiThreadTest
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/navigation/navigation-ui/src/main/res/menu/menu.xml b/navigation/navigation-ui/src/androidTest/res/menu/menu.xml
similarity index 100%
rename from navigation/navigation-ui/src/main/res/menu/menu.xml
rename to navigation/navigation-ui/src/androidTest/res/menu/menu.xml
diff --git a/navigation/navigation-ui/src/main/res/navigation/menu_item_graph.xml b/navigation/navigation-ui/src/androidTest/res/navigation/menu_item_graph.xml
similarity index 100%
rename from navigation/navigation-ui/src/main/res/navigation/menu_item_graph.xml
rename to navigation/navigation-ui/src/androidTest/res/navigation/menu_item_graph.xml
diff --git a/navigation/navigation-ui/src/main/res/navigation/simple_graph.xml b/navigation/navigation-ui/src/androidTest/res/navigation/simple_graph.xml
similarity index 100%
rename from navigation/navigation-ui/src/main/res/navigation/simple_graph.xml
rename to navigation/navigation-ui/src/androidTest/res/navigation/simple_graph.xml
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt
index 2b1d466..f684502 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt
@@ -16,6 +16,7 @@
 
 package androidx.privacysandbox.tools.apicompiler
 
+import androidx.privacysandbox.tools.apicompiler.generator.SandboxApiVersion
 import androidx.privacysandbox.tools.apicompiler.generator.SdkCodeGenerator
 import androidx.privacysandbox.tools.apicompiler.parser.ApiParser
 import com.google.devtools.ksp.processing.CodeGenerator
@@ -31,10 +32,10 @@
     private val logger: KSPLogger,
     private val codeGenerator: CodeGenerator,
     private val options: Map<String, String>,
-) :
-    SymbolProcessor {
+) : SymbolProcessor {
     companion object {
         const val AIDL_COMPILER_PATH_OPTIONS_KEY = "aidl_compiler_path"
+        const val USE_COMPAT_LIBRARY_OPTIONS_KEY = "use_sdk_runtime_compat_library"
     }
 
     var invoked = false
@@ -51,9 +52,18 @@
             return emptyList()
         }
 
+        val target = if (options[USE_COMPAT_LIBRARY_OPTIONS_KEY]?.lowercase() == "true") {
+            SandboxApiVersion.SDK_RUNTIME_COMPAT_LIBRARY
+        } else SandboxApiVersion.API_33
+
         val parsedApi = ApiParser(resolver, logger).parseApi()
 
-        SdkCodeGenerator(codeGenerator, parsedApi, path).generate()
+        SdkCodeGenerator(
+            codeGenerator,
+            parsedApi,
+            path,
+            target,
+        ).generate()
         return emptyList()
     }
 
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt
index 856f8fd..25dd589 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt
@@ -18,7 +18,6 @@
 
 import androidx.privacysandbox.tools.core.generator.build
 import androidx.privacysandbox.tools.core.generator.poetSpec
-import androidx.privacysandbox.tools.core.generator.stubDelegateNameSpec
 import androidx.privacysandbox.tools.core.model.AnnotatedInterface
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.core.model.getOnlyService
@@ -26,25 +25,14 @@
 import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.FunSpec
 import com.squareup.kotlinpoet.KModifier
-import com.squareup.kotlinpoet.MemberName
 import com.squareup.kotlinpoet.TypeSpec
 
-class AbstractSdkProviderGenerator(private val api: ParsedApi) {
+/** Generates an SDK provider that delegates calls to SDK defined classes. */
+internal abstract class AbstractSdkProviderGenerator(protected val api: ParsedApi) {
+
     companion object {
-        private val sandboxedSdkProviderClass =
-            ClassName("androidx.privacysandbox.sdkruntime.core", "SandboxedSdkProviderCompat")
-        private val sandboxedSdkClass =
-            ClassName("androidx.privacysandbox.sdkruntime.core", "SandboxedSdkCompat")
-        private val sandboxedSdkCreateMethod =
-            MemberName(
-                ClassName(
-                    sandboxedSdkClass.packageName,
-                    sandboxedSdkClass.simpleName,
-                    "Companion"
-                ), "create"
-            )
-        private val contextClass = ClassName("android.content", "Context")
-        private val bundleClass = ClassName("android.os", "Bundle")
+        val contextClass = ClassName("android.content", "Context")
+        val bundleClass = ClassName("android.os", "Bundle")
         private val viewClass = ClassName("android.view", "View")
     }
 
@@ -56,7 +44,7 @@
         val className = "AbstractSandboxedSdkProvider"
         val classSpec =
             TypeSpec.classBuilder(className)
-                .superclass(sandboxedSdkProviderClass)
+                .superclass(superclassName)
                 .addModifiers(KModifier.ABSTRACT)
                 .addFunction(generateOnLoadSdkFunction())
                 .addFunction(generateGetViewFunction())
@@ -67,21 +55,11 @@
             .build()
     }
 
-    private fun generateOnLoadSdkFunction(): FunSpec {
-        return FunSpec.builder("onLoadSdk").build {
-            addModifiers(KModifier.OVERRIDE)
-            addParameter("params", bundleClass)
-            returns(sandboxedSdkClass)
-            addStatement(
-                "val sdk = ${getCreateServiceFunctionName(api.getOnlyService())}(context!!)"
-            )
-            addStatement(
-                "return %M(%T(sdk))",
-                sandboxedSdkCreateMethod,
-                api.getOnlyService().stubDelegateNameSpec()
-            )
-        }
-    }
+    abstract val superclassName: ClassName
+    abstract fun generateOnLoadSdkFunction(): FunSpec
+
+    protected fun createServiceFunctionName(service: AnnotatedInterface) =
+        "create${service.type.simpleName}"
 
     private fun generateGetViewFunction(): FunSpec {
         return FunSpec.builder("getView").build {
@@ -96,13 +74,10 @@
     }
 
     private fun generateCreateServiceFunction(service: AnnotatedInterface): FunSpec {
-        return FunSpec.builder(getCreateServiceFunctionName(service))
+        return FunSpec.builder(createServiceFunctionName(service))
             .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
             .addParameter("context", contextClass)
             .returns(service.type.poetSpec())
             .build()
     }
-
-    private fun getCreateServiceFunctionName(service: AnnotatedInterface) =
-        "create${service.type.simpleName}"
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt
new file mode 100644
index 0000000..d22bbda
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 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.privacysandbox.tools.apicompiler.generator
+
+import androidx.privacysandbox.tools.core.generator.build
+import androidx.privacysandbox.tools.core.generator.stubDelegateNameSpec
+import androidx.privacysandbox.tools.core.model.ParsedApi
+import androidx.privacysandbox.tools.core.model.getOnlyService
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
+
+/** SDK provider generator that uses Android API 33 to communicate with the Privacy Sandbox. */
+internal class Api33SdkProviderGenerator(parsedApi: ParsedApi) :
+    AbstractSdkProviderGenerator(parsedApi) {
+    companion object {
+        private val sandboxedSdkClass =
+            ClassName("android.app.sdksandbox", "SandboxedSdk")
+    }
+
+    override val superclassName =
+        ClassName("android.app.sdksandbox", "SandboxedSdkProvider")
+
+    override fun generateOnLoadSdkFunction() = FunSpec.builder("onLoadSdk").build {
+        addModifiers(KModifier.OVERRIDE)
+        addParameter("params", bundleClass)
+        returns(sandboxedSdkClass)
+        addStatement(
+            "val sdk = ${createServiceFunctionName(api.getOnlyService())}(context!!)"
+        )
+        addStatement(
+            "return %T(%T(sdk))",
+            sandboxedSdkClass,
+            api.getOnlyService().stubDelegateNameSpec()
+        )
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt
new file mode 100644
index 0000000..9934deb
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 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.privacysandbox.tools.apicompiler.generator
+
+import androidx.privacysandbox.tools.core.generator.build
+import androidx.privacysandbox.tools.core.generator.stubDelegateNameSpec
+import androidx.privacysandbox.tools.core.model.ParsedApi
+import androidx.privacysandbox.tools.core.model.getOnlyService
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.MemberName
+
+/** SDK Provider generator that uses the SDK Runtime library to communicate with the sandbox. */
+internal class CompatSdkProviderGenerator(parsedApi: ParsedApi) :
+    AbstractSdkProviderGenerator(parsedApi) {
+    companion object {
+        private val sandboxedSdkCompatClass =
+            ClassName("androidx.privacysandbox.sdkruntime.core", "SandboxedSdkCompat")
+        private val sandboxedSdkCompatCreateMethod =
+            MemberName(
+                ClassName(
+                    sandboxedSdkCompatClass.packageName,
+                    sandboxedSdkCompatClass.simpleName,
+                    "Companion"
+                ), "create"
+            )
+    }
+
+    override val superclassName =
+        ClassName("androidx.privacysandbox.sdkruntime.core", "SandboxedSdkProviderCompat")
+
+    override fun generateOnLoadSdkFunction() = FunSpec.builder("onLoadSdk").build {
+        addModifiers(KModifier.OVERRIDE)
+        addParameter("params", bundleClass)
+        returns(sandboxedSdkCompatClass)
+        addStatement(
+            "val sdk = ${createServiceFunctionName(api.getOnlyService())}(context!!)"
+        )
+        addStatement(
+            "return %M(%T(sdk))",
+            sandboxedSdkCompatCreateMethod,
+            api.getOnlyService().stubDelegateNameSpec()
+        )
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
index 0d1b157..f278f79 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
@@ -36,10 +36,19 @@
 import kotlin.io.path.extension
 import kotlin.io.path.nameWithoutExtension
 
-class SdkCodeGenerator(
+internal enum class SandboxApiVersion {
+    // Android 13 - Tiramisu Privacy Sandbox
+    API_33,
+
+    // SDK runtime backwards compatibility library.
+    SDK_RUNTIME_COMPAT_LIBRARY,
+}
+
+internal class SdkCodeGenerator(
     private val codeGenerator: CodeGenerator,
     private val api: ParsedApi,
     private val aidlCompilerPath: Path,
+    private val sandboxApiVersion: SandboxApiVersion
 ) {
     private val binderCodeConverter = ServerBinderCodeConverter(api)
 
@@ -77,7 +86,11 @@
     }
 
     private fun generateAbstractSdkProvider() {
-        AbstractSdkProviderGenerator(api).generate()?.also(::write)
+        val generator = when (sandboxApiVersion) {
+            SandboxApiVersion.API_33 -> Api33SdkProviderGenerator(api)
+            SandboxApiVersion.SDK_RUNTIME_COMPAT_LIBRARY -> CompatSdkProviderGenerator(api)
+        }
+        generator.generate()?.also(::write)
     }
 
     private fun generateStubDelegates() {
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkTest.kt
new file mode 100644
index 0000000..78f636c
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 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.privacysandbox.tools.apicompiler
+
+import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
+import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/** Test the Privacy Sandbox API Compiler with an SDK that uses all available features. */
+@RunWith(JUnit4::class)
+class FullFeaturedSdkTest {
+
+    @Test
+    fun compileServiceInterface_ok() {
+        val inputTestDataDir = File("src/test/test-data/fullfeaturedsdk/input")
+        val outputTestDataDir = File("src/test/test-data/fullfeaturedsdk/output")
+        val inputSources = loadSourcesFromDirectory(inputTestDataDir)
+        val expectedKotlinSources = loadSourcesFromDirectory(outputTestDataDir)
+
+        val result = compileWithPrivacySandboxKspCompiler(inputSources)
+        assertThat(result).succeeds()
+
+        val expectedAidlFilepath = listOf(
+            "com/mysdk/ICancellationSignal.java",
+            "com/mysdk/IMyCallback.java",
+            "com/mysdk/IMyInterface.java",
+            "com/mysdk/IMyInterfaceTransactionCallback.java",
+            "com/mysdk/IMySdk.java",
+            "com/mysdk/IMySecondInterface.java",
+            "com/mysdk/IMySecondInterfaceTransactionCallback.java",
+            "com/mysdk/IResponseTransactionCallback.java",
+            "com/mysdk/IStringTransactionCallback.java",
+            "com/mysdk/IUnitTransactionCallback.java",
+            "com/mysdk/ParcelableRequest.java",
+            "com/mysdk/ParcelableResponse.java",
+            "com/mysdk/ParcelableStackFrame.java",
+            "com/mysdk/PrivacySandboxThrowableParcel.java",
+        )
+        assertThat(result).hasAllExpectedGeneratedSourceFilesAndContent(
+            expectedKotlinSources,
+            expectedAidlFilepath
+        )
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
index 2aaaadc..77d6c2d 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
@@ -18,72 +18,22 @@
 
 import androidx.privacysandbox.tools.core.proto.PrivacySandboxToolsProtocol.ToolMetadata
 import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
-import androidx.privacysandbox.tools.testing.CompilationTestHelper.compileAll
-import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
 import androidx.privacysandbox.tools.testing.resourceOutputDir
 import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
-import androidx.room.compiler.processing.util.compiler.compile
 import com.google.common.truth.Truth.assertThat
-import java.io.File
-import java.nio.file.Files
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
 class PrivacySandboxKspCompilerTest {
-    @Test
-    fun compileServiceInterface_ok() {
-        val inputTestDataDir = File("src/test/test-data/testinterface/input")
-        val outputTestDataDir = File("src/test/test-data/testinterface/output")
-        val inputSources = loadSourcesFromDirectory(inputTestDataDir)
-        val expectedKotlinSources = loadSourcesFromDirectory(outputTestDataDir)
-        val provider = PrivacySandboxKspCompiler.Provider()
-
-        val result = compileAll(
-            inputSources,
-            symbolProcessorProviders = listOf(provider),
-            processorOptions = getProcessorOptions(),
-        )
-        assertThat(result).succeeds()
-
-        val expectedAidlFilepath = listOf(
-            "com/mysdk/ICancellationSignal.java",
-            "com/mysdk/IMyCallback.java",
-            "com/mysdk/IMyInterface.java",
-            "com/mysdk/IMyInterfaceTransactionCallback.java",
-            "com/mysdk/IMySdk.java",
-            "com/mysdk/IMySecondInterface.java",
-            "com/mysdk/IMySecondInterfaceTransactionCallback.java",
-            "com/mysdk/IResponseTransactionCallback.java",
-            "com/mysdk/IStringTransactionCallback.java",
-            "com/mysdk/IUnitTransactionCallback.java",
-            "com/mysdk/ParcelableRequest.java",
-            "com/mysdk/ParcelableResponse.java",
-            "com/mysdk/ParcelableStackFrame.java",
-            "com/mysdk/PrivacySandboxThrowableParcel.java",
-        )
-        assertThat(result).hasAllExpectedGeneratedSourceFilesAndContent(
-            expectedKotlinSources,
-            expectedAidlFilepath
-        )
-    }
 
     @Test
     fun compileEmpty_ok() {
-        val provider = PrivacySandboxKspCompiler.Provider()
-        // Check that compilation is successful
-        assertThat(
-            compile(
-                Files.createTempDirectory("test").toFile(),
-                TestCompilationArguments(
-                    sources = emptyList(),
-                    symbolProcessorProviders = listOf(provider),
-                    processorOptions = getProcessorOptions(),
-                )
-            )
-        ).hasNoGeneratedSourceFiles()
+        assertThat(compileWithPrivacySandboxKspCompiler(listOf())).apply {
+            succeeds()
+            hasNoGeneratedSourceFiles()
+        }
     }
 
     @Test
@@ -100,13 +50,7 @@
                     }
                 """
             )
-        val provider = PrivacySandboxKspCompiler.Provider()
-        val compilationResult =
-            compileAll(
-                listOf(source),
-                symbolProcessorProviders = listOf(provider),
-                processorOptions = getProcessorOptions(),
-            )
+        val compilationResult = compileWithPrivacySandboxKspCompiler(listOf(source))
         assertThat(compilationResult).succeeds()
 
         val resourceMap = compilationResult.resourceOutputDir.walk()
@@ -138,20 +82,6 @@
                     }
                 """
             )
-        val provider = PrivacySandboxKspCompiler.Provider()
-        // Check that compilation fails
-        assertThat(
-            compileAll(
-                listOf(source),
-                symbolProcessorProviders = listOf(provider),
-                processorOptions = getProcessorOptions(),
-            )
-        ).fails()
+        assertThat(compileWithPrivacySandboxKspCompiler(listOf(source))).fails()
     }
-
-    private fun getProcessorOptions() =
-        mapOf(
-            "aidl_compiler_path" to (System.getProperty("aidl_compiler_path")
-                ?: throw IllegalArgumentException("aidl_compiler_path flag not set."))
-        )
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkRuntimeLibrarySdkTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkRuntimeLibrarySdkTest.kt
new file mode 100644
index 0000000..cda0661
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkRuntimeLibrarySdkTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 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.privacysandbox.tools.apicompiler
+
+import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
+import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class SdkRuntimeLibrarySdkTest {
+    @Test
+    fun compileServiceInterface_ok() {
+        val inputTestDataDir = File("src/test/test-data/sdkruntimelibrarysdk/input")
+        val outputTestDataDir = File("src/test/test-data/sdkruntimelibrarysdk/output")
+        val inputSources = loadSourcesFromDirectory(inputTestDataDir)
+        val expectedKotlinSources = loadSourcesFromDirectory(outputTestDataDir)
+
+        val result = compileWithPrivacySandboxKspCompiler(
+            inputSources,
+            platformStubs = PlatformStubs.SDK_RUNTIME_LIBRARY,
+            extraProcessorOptions = mapOf("use_sdk_runtime_compat_library" to "true")
+        )
+        assertThat(result).succeeds()
+
+        val expectedAidlFilepath = listOf(
+            "com/mysdk/ICancellationSignal.java",
+            "com/mysdk/IBackwardsCompatibleSdk.java",
+            "com/mysdk/IStringTransactionCallback.java",
+            "com/mysdk/ParcelableStackFrame.java",
+            "com/mysdk/PrivacySandboxThrowableParcel.java",
+        )
+        assertThat(result).hasAllExpectedGeneratedSourceFilesAndContent(
+            expectedKotlinSources,
+            expectedAidlFilepath
+        )
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
new file mode 100644
index 0000000..5de917b
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2022 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.privacysandbox.tools.apicompiler
+
+import androidx.privacysandbox.tools.testing.CompilationTestHelper
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.compiler.TestCompilationResult
+
+/**
+ * Compile the given sources using the PrivacySandboxKspCompiler.
+ *
+ * Default parameters will set required options like AIDL compiler path and use the latest
+ * Android platform API stubs that support the Privacy Sandbox.
+ */
+fun compileWithPrivacySandboxKspCompiler(
+    sources: List<Source>,
+    platformStubs: PlatformStubs = PlatformStubs.API_33,
+    extraProcessorOptions: Map<String, String> = mapOf(),
+): TestCompilationResult {
+    val provider = PrivacySandboxKspCompiler.Provider()
+
+    val processorOptions = buildMap {
+        val aidlPath = (System.getProperty("aidl_compiler_path")
+            ?: throw IllegalArgumentException("aidl_compiler_path flag not set."))
+        put("aidl_compiler_path", aidlPath)
+        putAll(extraProcessorOptions)
+    }
+
+    return CompilationTestHelper.compileAll(
+        sources + platformStubs.sources,
+        symbolProcessorProviders = listOf(provider),
+        processorOptions = processorOptions,
+    )
+}
+
+enum class PlatformStubs(val sources: List<Source>) {
+    API_33(syntheticApi33PrivacySandboxStubs),
+    SDK_RUNTIME_LIBRARY(syntheticSdkRuntimeLibraryStubs),
+}
+
+// SDK Runtime library is not available in AndroidX prebuilts, so while that's the case we use fake
+// stubs to run our compilation tests.
+private val syntheticSdkRuntimeLibraryStubs = listOf(
+    Source.kotlin(
+        "androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt", """
+        |package androidx.privacysandbox.sdkruntime.core
+        |
+        |import android.os.IBinder
+        |
+        |@Suppress("UNUSED_PARAMETER")
+        |sealed class SandboxedSdkCompat {
+        |    abstract fun getInterface(): IBinder?
+        |
+        |    companion object {
+        |        fun create(binder: IBinder): SandboxedSdkCompat = throw RuntimeException("Stub!")
+        |    }
+        |}
+        |""".trimMargin()
+    ),
+    Source.kotlin(
+        "androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderCompat.kt", """
+        |package androidx.privacysandbox.sdkruntime.core
+        |
+        |import android.content.Context
+        |import android.os.Bundle
+        |import android.view.View
+        |
+        |@Suppress("UNUSED_PARAMETER")
+        |abstract class SandboxedSdkProviderCompat {
+        |   var context: Context? = null
+        |       private set
+        |   fun attachContext(context: Context): Unit = throw RuntimeException("Stub!")
+        |
+        |   abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
+        |
+        |   open fun beforeUnloadSdk() {}
+        |
+        |   abstract fun getView(
+        |       windowContext: Context,
+        |       params: Bundle,
+        |       width: Int,
+        |       height: Int
+        |   ): View
+        |}
+        |""".trimMargin()
+    )
+)
+
+// PrivacySandbox platform APIs are not available in AndroidX prebuilts nor are they stable, so
+// while that's the case we use fake stubs to run our compilation tests.
+val syntheticApi33PrivacySandboxStubs = listOf(
+    Source.java(
+        "android.app.sdksandbox.SandboxedSdk", """
+        |package android.app.sdksandbox;
+        |
+        |import android.os.IBinder;
+        |
+        |public final class SandboxedSdk {
+        |    public SandboxedSdk(IBinder sdkInterface) {}
+        |    public IBinder getInterface() { throw new RuntimeException("Stub!"); }
+        |}
+        |""".trimMargin()
+    ),
+    Source.java(
+        "android.app.sdksandbox.SandboxedSdkProvider", """
+        |package android.app.sdksandbox;
+        |
+        |import android.content.Context;
+        |import android.os.Bundle;
+        |import android.view.View;
+        |
+        |public abstract class SandboxedSdkProvider {
+        |    public final void attachContext(Context context) {
+        |        throw new RuntimeException("Stub!");
+        |    }
+        |    public final Context getContext() {
+        |        throw new RuntimeException("Stub!");
+        |    }
+        |    public abstract SandboxedSdk onLoadSdk(Bundle params)
+        |        throws LoadSdkException;
+        |
+        |    public void beforeUnloadSdk() {}
+        |
+        |    public abstract View getView(
+        |        Context windowContext, Bundle params, int width, int height);
+        |}
+        |""".trimMargin()
+    ),
+    Source.java(
+        "android.app.sdksandbox.LoadSdkException", """
+        |package android.app.sdksandbox;
+        |
+        |@SuppressWarnings("serial")
+        |public final class LoadSdkException extends Exception {}
+        |""".trimMargin()
+    ),
+    Source.java(
+        "android.app.sdksandbox.SandboxedSdkContext", """
+        |package android.app.sdksandbox;
+        |
+        |public final class SandboxedSdkContext {}
+        |""".trimMargin()
+    ),
+)
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/input/com/mysdk/MySdk.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/input/com/mysdk/MySdk.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/AbstractSandboxedSdkProvider.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/AbstractSandboxedSdkProvider.kt
new file mode 100644
index 0000000..43c54ac
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/AbstractSandboxedSdkProvider.kt
@@ -0,0 +1,26 @@
+package com.mysdk
+
+import android.app.sdksandbox.SandboxedSdk
+import android.app.sdksandbox.SandboxedSdkProvider
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import kotlin.Int
+
+public abstract class AbstractSandboxedSdkProvider : SandboxedSdkProvider() {
+  public override fun onLoadSdk(params: Bundle): SandboxedSdk {
+    val sdk = createMySdk(context!!)
+    return SandboxedSdk(MySdkStubDelegate(sdk))
+  }
+
+  public override fun getView(
+    windowContext: Context,
+    params: Bundle,
+    width: Int,
+    height: Int,
+  ): View {
+    TODO("Implement")
+  }
+
+  protected abstract fun createMySdk(context: Context): MySdk
+}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MyCallbackClientProxy.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MyCallbackClientProxy.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MyInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyInterfaceStubDelegate.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MyInterfaceStubDelegate.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyInterfaceStubDelegate.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySdkStubDelegate.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySecondInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySecondInterfaceStubDelegate.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySecondInterfaceStubDelegate.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySecondInterfaceStubDelegate.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
similarity index 93%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 368f02f..e168416 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -14,7 +14,7 @@
             stackFrame
         }.toTypedArray()
         throwable.cause?.let {
-            parcel.cause = toThrowableParcel(it)
+            parcel.cause = arrayOf(toThrowableParcel(it))
         }
         parcel.suppressedExceptions =
             throwable.suppressedExceptions.map {
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/RequestConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/RequestConverter.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/RequestConverter.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/RequestConverter.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/ResponseConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/ResponseConverter.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/ResponseConverter.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/ResponseConverter.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/TransportCancellationCallback.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/TransportCancellationCallback.kt
similarity index 100%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/TransportCancellationCallback.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/TransportCancellationCallback.kt
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/input/com/mysdk/BackwardsCompatibleSdk.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/input/com/mysdk/BackwardsCompatibleSdk.kt
new file mode 100644
index 0000000..8424489
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/input/com/mysdk/BackwardsCompatibleSdk.kt
@@ -0,0 +1,11 @@
+package com.mysdk
+
+import androidx.privacysandbox.tools.PrivacySandboxCallback
+import androidx.privacysandbox.tools.PrivacySandboxInterface
+import androidx.privacysandbox.tools.PrivacySandboxService
+import androidx.privacysandbox.tools.PrivacySandboxValue
+
+@PrivacySandboxService
+interface BackwardsCompatibleSdk {
+    suspend fun doStuff(x: Int, y: Int): String
+}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/AbstractSandboxedSdkProvider.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/AbstractSandboxedSdkProvider.kt
similarity index 75%
rename from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/AbstractSandboxedSdkProvider.kt
rename to privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/AbstractSandboxedSdkProvider.kt
index 6efe8a0..5a0d588 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/AbstractSandboxedSdkProvider.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/AbstractSandboxedSdkProvider.kt
@@ -10,8 +10,8 @@
 
 public abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat() {
   public override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
-    val sdk = createMySdk(context!!)
-    return create(MySdkStubDelegate(sdk))
+    val sdk = createBackwardsCompatibleSdk(context!!)
+    return create(BackwardsCompatibleSdkStubDelegate(sdk))
   }
 
   public override fun getView(
@@ -23,5 +23,5 @@
     TODO("Implement")
   }
 
-  protected abstract fun createMySdk(context: Context): MySdk
+  protected abstract fun createBackwardsCompatibleSdk(context: Context): BackwardsCompatibleSdk
 }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/BackwardsCompatibleSdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/BackwardsCompatibleSdkStubDelegate.kt
new file mode 100644
index 0000000..bca930d
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/BackwardsCompatibleSdkStubDelegate.kt
@@ -0,0 +1,32 @@
+package com.mysdk
+
+import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
+import kotlin.Int
+import kotlin.Unit
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+
+public class BackwardsCompatibleSdkStubDelegate internal constructor(
+  public val `delegate`: BackwardsCompatibleSdk,
+) : IBackwardsCompatibleSdk.Stub() {
+  public override fun doStuff(
+    x: Int,
+    y: Int,
+    transactionCallback: IStringTransactionCallback,
+  ): Unit {
+    @OptIn(DelicateCoroutinesApi::class)
+    val job = GlobalScope.launch(Dispatchers.Main) {
+      try {
+        val result = delegate.doStuff(x, y)
+        transactionCallback.onSuccess(result)
+      }
+      catch (t: Throwable) {
+        transactionCallback.onFailure(toThrowableParcel(t))
+      }
+    }
+    val cancellationSignal = TransportCancellationCallback() { job.cancel() }
+    transactionCallback.onCancellable(cancellationSignal)
+  }
+}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
similarity index 93%
copy from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
copy to privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 368f02f..e168416 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -14,7 +14,7 @@
             stackFrame
         }.toTypedArray()
         throwable.cause?.let {
-            parcel.cause = toThrowableParcel(it)
+            parcel.cause = arrayOf(toThrowableParcel(it))
         }
         parcel.suppressedExceptions =
             throwable.suppressedExceptions.map {
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/TransportCancellationCallback.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/TransportCancellationCallback.kt
similarity index 100%
copy from privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/TransportCancellationCallback.kt
copy to privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/TransportCancellationCallback.kt
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
index 4b0857c..73d230c 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
@@ -9,7 +9,10 @@
         val stackTrace = throwableParcel.stackTrace
         val exception = PrivacySandboxException(
             "[$exceptionClass] $errorMessage",
-            throwableParcel.cause?.let { fromThrowableParcel(it) })
+            throwableParcel.cause?.firstOrNull()?.let {
+                fromThrowableParcel(it)
+            }
+        )
         for (suppressed in throwableParcel.suppressedExceptions) {
             exception.addSuppressed(fromThrowableParcel(suppressed))
         }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 42297ec..7797726 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -9,7 +9,10 @@
         val stackTrace = throwableParcel.stackTrace
         val exception = PrivacySandboxException(
             "[$exceptionClass] $errorMessage",
-            throwableParcel.cause?.let { fromThrowableParcel(it) })
+            throwableParcel.cause?.firstOrNull()?.let {
+                fromThrowableParcel(it)
+            }
+        )
         for (suppressed in throwableParcel.suppressedExceptions) {
             exception.addSuppressed(fromThrowableParcel(suppressed))
         }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
index 34de1d7..d86cb2b 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
@@ -9,7 +9,10 @@
         val stackTrace = throwableParcel.stackTrace
         val exception = PrivacySandboxException(
             "[$exceptionClass] $errorMessage",
-            throwableParcel.cause?.let { fromThrowableParcel(it) })
+            throwableParcel.cause?.firstOrNull()?.let {
+                fromThrowableParcel(it)
+            }
+        )
         for (suppressed in throwableParcel.suppressedExceptions) {
             exception.addSuppressed(fromThrowableParcel(suppressed))
         }
diff --git a/privacysandbox/tools/tools-apipackager/src/test/java/androidx/privacysandbox/tools/apipackager/PrivacySandboxApiPackagerTest.kt b/privacysandbox/tools/tools-apipackager/src/test/java/androidx/privacysandbox/tools/apipackager/PrivacySandboxApiPackagerTest.kt
index 10a9cad..d29527c 100644
--- a/privacysandbox/tools/tools-apipackager/src/test/java/androidx/privacysandbox/tools/apipackager/PrivacySandboxApiPackagerTest.kt
+++ b/privacysandbox/tools/tools-apipackager/src/test/java/androidx/privacysandbox/tools/apipackager/PrivacySandboxApiPackagerTest.kt
@@ -132,7 +132,7 @@
             |interface Valid
         """.trimMargin()
         )
-        val sdkClasspath = compileAll(listOf(source), includePrivacySandboxPlatformSources = false)
+        val sdkClasspath = compileAll(listOf(source))
             .outputClasspath.first().toPath()
 
         val metadataPath = sdkClasspath.resolve(Metadata.filePath).also {
@@ -186,7 +186,7 @@
             |interface Valid
         """.trimMargin()
         )
-        val sdkClasspath = compileAll(listOf(source), includePrivacySandboxPlatformSources = false)
+        val sdkClasspath = compileAll(listOf(source))
             .outputClasspath.first().toPath()
         val descriptorPathThatAlreadyExists =
             makeTestDirectory().resolve("sdk-descriptors.jar").also {
@@ -201,7 +201,7 @@
 
     /** Compiles the given source file and returns a classpath with the results. */
     private fun compileAndReturnUnzippedPackagedClasspath(source: Source): File {
-        val result = compileAll(listOf(source), includePrivacySandboxPlatformSources = false)
+        val result = compileAll(listOf(source))
         assertThat(result).succeeds()
         assertThat(result.outputClasspath).hasSize(1)
 
@@ -234,7 +234,6 @@
         return compileAll(
             listOf(source),
             extraClasspath = listOf(extraClasspath),
-            includePrivacySandboxPlatformSources = false
         )
     }
 }
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
index 4c245ac..1df8157 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
@@ -170,7 +170,7 @@
             addProperty("exceptionClass", primitive("String"))
             addProperty("errorMessage", primitive("String"))
             addProperty("stackTrace", AidlTypeSpec(parcelableStackFrameType(), isList = true))
-            addProperty("cause", AidlTypeSpec(throwableParcelType()), isNullable = true)
+            addProperty("cause", AidlTypeSpec(throwableParcelType(), isList = true))
             addProperty(
                 "suppressedExceptions", AidlTypeSpec(throwableParcelType(), isList = true)
             )
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
index 4ac7844..b45aca8 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
@@ -77,7 +77,7 @@
                         stackFrame
                     }.toTypedArray()
                     throwable.cause?.let {
-                        parcel.cause = ${toThrowableParcelNameSpec.simpleName}(it)
+                        parcel.cause = arrayOf(${toThrowableParcelNameSpec.simpleName}(it))
                     }
                     parcel.suppressedExceptions =
                         throwable.suppressedExceptions.map {
@@ -103,7 +103,10 @@
                     val stackTrace = throwableParcel.stackTrace
                     val exception = PrivacySandboxException(
                         "[${'$'}exceptionClass] ${'$'}errorMessage",
-                        throwableParcel.cause?.let { ${fromThrowableParcelNameSpec.simpleName}(it) })
+                        throwableParcel.cause?.firstOrNull()?.let {
+                            ${fromThrowableParcelNameSpec.simpleName}(it)
+                        }
+                    )
                     for (suppressed in throwableParcel.suppressedExceptions) {
                         exception.addSuppressed(${fromThrowableParcelNameSpec.simpleName}(suppressed))
                     }
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index 716a0f9..cff4771 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -3,8 +3,8 @@
 import com.mysdk.ParcelableStackFrame;
 
 parcelable PrivacySandboxThrowableParcel {
-    @nullable(heap=true) PrivacySandboxThrowableParcel cause;
     ParcelableStackFrame[] stackTrace;
+    PrivacySandboxThrowableParcel[] cause;
     PrivacySandboxThrowableParcel[] suppressedExceptions;
     String errorMessage;
     String exceptionClass;
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index 716a0f9..cff4771 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -3,8 +3,8 @@
 import com.mysdk.ParcelableStackFrame;
 
 parcelable PrivacySandboxThrowableParcel {
-    @nullable(heap=true) PrivacySandboxThrowableParcel cause;
     ParcelableStackFrame[] stackTrace;
+    PrivacySandboxThrowableParcel[] cause;
     PrivacySandboxThrowableParcel[] suppressedExceptions;
     String errorMessage;
     String exceptionClass;
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index 716a0f9..cff4771 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -3,8 +3,8 @@
 import com.mysdk.ParcelableStackFrame;
 
 parcelable PrivacySandboxThrowableParcel {
-    @nullable(heap=true) PrivacySandboxThrowableParcel cause;
     ParcelableStackFrame[] stackTrace;
+    PrivacySandboxThrowableParcel[] cause;
     PrivacySandboxThrowableParcel[] suppressedExceptions;
     String errorMessage;
     String exceptionClass;
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
index f2a5114..0a9c958 100644
--- a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
@@ -39,18 +39,14 @@
     fun compileAll(
         sources: List<Source>,
         extraClasspath: List<File> = emptyList(),
-        includePrivacySandboxPlatformSources: Boolean = true,
         symbolProcessorProviders: List<SymbolProcessorProvider> = emptyList(),
         processorOptions: Map<String, String> = emptyMap()
     ): TestCompilationResult {
         val tempDir = Files.createTempDirectory("compile").toFile().also { it.deleteOnExit() }
-        val targetSources = if (includePrivacySandboxPlatformSources) {
-            sources + syntheticPrivacySandboxSources
-        } else sources
         return compile(
             tempDir,
             TestCompilationArguments(
-                sources = targetSources,
+                sources = sources,
                 classpath = extraClasspath,
                 symbolProcessorProviders = symbolProcessorProviders,
                 processorOptions = processorOptions,
@@ -154,73 +150,3 @@
         }
     }
 }
-
-// PrivacySandbox platform APIs are not available in AndroidX prebuilts nor are they stable, so
-// while that's the case we use fake stubs to run our compilation tests.
-val syntheticPrivacySandboxSources = listOf(
-    Source.kotlin(
-        "androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt", """
-        |package androidx.privacysandbox.sdkruntime.core
-        |
-        |import android.os.IBinder
-        |
-        |@Suppress("UNUSED_PARAMETER")
-        |sealed class SandboxedSdkCompat {
-        |    abstract fun getInterface(): IBinder?
-        |
-        |    companion object {
-        |        fun create(binder: IBinder): SandboxedSdkCompat = throw RuntimeException("Stub!")
-        |    }
-        |}
-        |""".trimMargin()
-    ),
-    Source.kotlin(
-        "androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt", """
-        |package androidx.privacysandbox.sdkruntime.client
-        |
-        |import android.content.Context
-        |import android.os.Bundle
-        |import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
-        |
-        |@Suppress("UNUSED_PARAMETER")
-        |class SdkSandboxManagerCompat private constructor() {
-        |    suspend fun loadSdk(
-        |        sdkName: String,
-        |        params: Bundle,
-        |    ): SandboxedSdkCompat = throw RuntimeException("Stub!")
-        |
-        |    companion object {
-        |        fun obtain(context: Context): SdkSandboxManagerCompat =
-        |            throw RuntimeException("Stub!")
-        |    }
-        |}
-        |""".trimMargin()
-    ),
-    Source.kotlin(
-        "androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderCompat.kt", """
-        |package androidx.privacysandbox.sdkruntime.core
-        |
-        |import android.content.Context
-        |import android.os.Bundle
-        |import android.view.View
-        |
-        |@Suppress("UNUSED_PARAMETER")
-        |abstract class SandboxedSdkProviderCompat {
-        |   var context: Context? = null
-        |       private set
-        |   fun attachContext(context: Context): Unit = throw RuntimeException("Stub!")
-        |
-        |   abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
-        |
-        |   open fun beforeUnloadSdk() {}
-        |
-        |   abstract fun getView(
-        |       windowContext: Context,
-        |       params: Bundle,
-        |       width: Int,
-        |       height: Int
-        |   ): View
-        |}
-        |""".trimMargin()
-    )
-)
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
index cedb6fb6..b79f94a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
@@ -60,7 +60,11 @@
     val nullability: XNullability
 
     /**
-     * The resolved types of the super classes/interfaces of this type.
+     * The resolved direct super types of this type.
+     *
+     * The interface types, if any, will appear last in the list.
+     *
+     * See [Types#directSupertypes()](https://docs.oracle.com/javase/7/docs/api/javax/lang/model/util/Types.html#directSupertypes(javax.lang.model.type.TypeMirror))
      */
     val superTypes: List<XType>
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
index 80525af..1faa32c9 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
@@ -46,13 +46,6 @@
         get() = superClass
 
     /**
-     * The direct super types of this element.
-     *
-     * See [JLS 4.10.2](https://docs.oracle.com/javase/specs/jls/se18/html/jls-4.html#jls-4.10.2)
-     */
-    val superTypes: List<XType>
-
-    /**
      * The super class of this element if it represents a class.
      */
     val superClass: XType?
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index 403d6fb..a2beb5a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -25,7 +25,6 @@
 import androidx.room.compiler.processing.XMemberContainer
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XNullability
-import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.XTypeParameterElement
 import androidx.room.compiler.processing.collectAllMethods
@@ -36,7 +35,6 @@
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
 import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.TypeName
 import com.squareup.kotlinpoet.javapoet.JClassName
 import javax.lang.model.element.ElementKind
 import javax.lang.model.element.TypeElement
@@ -195,17 +193,6 @@
         )
     }
 
-    override val superTypes: List<XType> by lazy {
-        buildList {
-            if (isInterface() && superInterfaces.isEmpty()) {
-                add(env.requireType(TypeName.OBJECT))
-            } else {
-                superClass?.let { add(it) }
-                addAll(superInterfaces)
-            }
-        }
-    }
-
     override val superClass: JavacType? by lazy {
         // javac models non-existing types as TypeKind.NONE but we prefer to make it nullable.
         // just makes more sense and safer as we don't need to check for none.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index 3e072561..3acfe4a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -26,9 +26,13 @@
 import com.google.devtools.ksp.KspExperimental
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeArgument
+import com.google.devtools.ksp.symbol.KSTypeParameter
 import com.google.devtools.ksp.symbol.KSTypeReference
 import com.google.devtools.ksp.symbol.Nullability
+import com.google.devtools.ksp.symbol.Variance
 import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.WildcardTypeName
 import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.KTypeName
 import kotlin.reflect.KClass
@@ -91,13 +95,60 @@
     }
 
     override val superTypes: List<XType> by lazy {
-        val declaration = ksType.declaration as? KSClassDeclaration
-        declaration?.superTypes?.toList()?.map {
+        if (typeName == TypeName.OBJECT) {
+            // The object class doesn't have any supertypes.
+            return@lazy emptyList<XType>()
+        }
+        val resolvedTypeArguments: Map<String, KSTypeArgument> =
+            ksType.declaration.typeParameters.mapIndexed { i, parameter ->
+                parameter.name.asString() to ksType.arguments[i]
+            }.toMap()
+        val superTypes = (ksType.declaration as? KSClassDeclaration)?.superTypes?.toList()?.map {
             env.wrap(
-                ksType = it.resolve(),
+                ksType = resolveTypeArguments(it.resolve(), resolvedTypeArguments),
                 allowPrimitives = false
             )
         } ?: emptyList()
+        val (superClasses, superInterfaces) = superTypes.partition {
+            it.typeElement?.isClass() == true
+        }
+        // Per documentation, always return the class before the interfaces.
+        if (superClasses.isEmpty()) {
+            // Return Object when there's no explicit super class specified on the class/interface.
+            // This matches javac's Types#directSupertypes().
+            listOf(env.requireType(TypeName.OBJECT)) + superInterfaces
+        } else {
+            check(superClasses.size == 1)
+            superClasses + superInterfaces
+        }
+    }
+
+    private fun resolveTypeArguments(
+        type: KSType,
+        resolvedTypeArguments: Map<String, KSTypeArgument>
+    ): KSType {
+        return type.replace(
+            type.arguments.map { argument ->
+                val argDeclaration = argument.type?.resolve()?.declaration
+                if (argDeclaration is KSTypeParameter) {
+                    // If this is a type parameter, replace it with the resolved type argument.
+                    resolvedTypeArguments[argDeclaration.name.asString()] ?: argument
+                } else if (
+                    argument.type != null && argument.type?.resolve()?.arguments?.isEmpty() == false
+                ) {
+                    // If this is a type with arguments, the arguments may contain a type parameter,
+                    // e.g. Foo<T>, so try to resolve the type and then convert to a type argument.
+                    env.resolver.getTypeArgument(
+                        env.resolver.createKSTypeReferenceFromKSType(
+                            resolveTypeArguments(argument.type!!.resolve(), resolvedTypeArguments)
+                        ),
+                        variance = Variance.INVARIANT
+                    )
+                } else {
+                    argument
+                }
+            }.toList()
+        )
     }
 
     override val typeElement by lazy {
@@ -136,7 +187,14 @@
     }
 
     override fun isError(): Boolean {
-        return ksType.isError
+        // Avoid returning true if this type represents a java wildcard type, e.g. "? extends Foo"
+        // since in that case the wildcard type is not the error type itself. Instead, the error
+        // type should be on the XType#extendsBound() type, "Foo", instead.
+        return ksType.isError && !isJavaWildcardType()
+    }
+
+    private fun isJavaWildcardType(): Boolean {
+        return asTypeName().java is WildcardTypeName
     }
 
     override fun defaultValue(): String {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 75517f7..7674249 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -45,7 +45,6 @@
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
 import com.google.devtools.ksp.symbol.Modifier
 import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.TypeName
 import com.squareup.kotlinpoet.javapoet.JClassName
 import com.squareup.kotlinpoet.javapoet.KClassName
 
@@ -89,17 +88,6 @@
         )
     }
 
-    override val superTypes: List<XType> by lazy {
-        buildList {
-            if (isInterface() && superInterfaces.isEmpty()) {
-                add(env.requireType(TypeName.OBJECT))
-            } else {
-                superClass?.let { add(it) }
-                addAll(superInterfaces)
-            }
-        }
-    }
-
     override val superClass: XType? by lazy {
         if (isInterface()) {
             // interfaces don't have super classes (they do have super types)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 36836e4..b49b301 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -145,7 +145,7 @@
                 assertThat(it.superClass).isEqualTo(
                     invocation.processingEnv.requireType("foo.bar.AbstractClass")
                 )
-                assertThat(it.superTypes).containsExactly(
+                assertThat(it.type.superTypes).containsExactly(
                     invocation.processingEnv.requireType("foo.bar.AbstractClass"),
                     invocation.processingEnv.requireType("foo.bar.MyInterface")
                 )
@@ -160,7 +160,7 @@
                 assertThat(it.superClass).isEqualTo(
                     invocation.processingEnv.requireType(JTypeName.OBJECT)
                 )
-                assertThat(it.superTypes).containsExactly(
+                assertThat(it.type.superTypes).containsExactly(
                     invocation.processingEnv.requireType(JTypeName.OBJECT)
                 )
                 assertThat(it.isAbstract()).isTrue()
@@ -171,7 +171,7 @@
             }
             invocation.processingEnv.requireTypeElement("foo.bar.MyInterface").let {
                 assertThat(it.superClass).isNull()
-                assertThat(it.superTypes).containsExactly(
+                assertThat(it.type.superTypes).containsExactly(
                     invocation.processingEnv.requireType(JTypeName.OBJECT)
                 )
                 assertThat(it.isInterface()).isTrue()
@@ -181,7 +181,8 @@
             }
             invocation.processingEnv.requireTypeElement("foo.bar.AnotherInterface").let {
                 assertThat(it.superClass).isNull()
-                assertThat(it.superTypes).containsExactly(
+                assertThat(it.type.superTypes).containsExactly(
+                    invocation.processingEnv.requireType("java.lang.Object"),
                     invocation.processingEnv.requireType("foo.bar.MyInterface")
                 )
                 assertThat(it.isInterface()).isTrue()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index b42a29c..4e5c8b2 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -37,6 +37,7 @@
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeVariableName
+import com.squareup.javapoet.WildcardTypeName
 import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.MUTABLE_SET
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
@@ -1037,4 +1038,273 @@
             """.trimIndent()
         ))) { it.checkPrimitiveType() }
     }
+
+    @Test
+    fun setSuperTypeNames() {
+        fun superTypeHierarchy(type: XType, depth: Int = 0): String {
+            val sb: StringBuilder = StringBuilder()
+            sb.append("${"  ".repeat(depth)}> ${type.typeName}")
+            type.superTypes.forEach {
+                sb.append("\n").append(superTypeHierarchy(it, depth + 1))
+            }
+            return sb.toString()
+        }
+
+        fun XTestInvocation.checkType() {
+            val fooElement = processingEnv.requireTypeElement("test.Foo")
+            val method1 = fooElement.getMethodByJvmName("method1")
+            val method2 = fooElement.getMethodByJvmName("method2")
+
+            // Check the return types of the unresolved methods
+            assertThat(method1.returnType.typeName).isEqualTo(TypeVariableName.get("T1"))
+            assertThat(method2.returnType.typeName).isEqualTo(TypeVariableName.get("T2"))
+
+            // Check the return types of the methods resolved into Usage
+            val usageElement = processingEnv.requireTypeElement("test.Usage")
+            assertThat(method1.asMemberOf(usageElement.type).returnType.typeName.toString())
+                .isEqualTo("test.Baz<java.lang.Long, java.lang.Number>")
+            assertThat(method2.asMemberOf(usageElement.type).returnType.typeName.toString())
+                .isEqualTo("java.lang.Integer")
+
+            // Check the supertypes of the unresolved Foo
+            assertThat(superTypeHierarchy(fooElement.type)).isEqualTo(
+                """
+                > test.Foo<V1, V2>
+                  > java.lang.Object
+                  > test.Bar<test.Baz<V1, java.lang.Number>, V2>
+                    > java.lang.Object
+                    > test.Baz<test.Baz<V1, java.lang.Number>, V2>
+                      > java.lang.Object
+                """.trimIndent()
+            )
+
+            // Check the supertypes of Foo<Long, Integer>
+            assertThat(superTypeHierarchy(usageElement.type)).isEqualTo(
+                """
+                > test.Usage
+                  > java.lang.Object
+                  > test.Foo<java.lang.Long, java.lang.Integer>
+                    > java.lang.Object
+                    > test.Bar<test.Baz<java.lang.Long, java.lang.Number>, java.lang.Integer>
+                      > java.lang.Object
+                      > test.Baz<test.Baz<java.lang.Long, java.lang.Number>, java.lang.Integer>
+                        > java.lang.Object
+                """.trimIndent()
+            )
+
+            // Check the supertypes of Foo<String, Integer>
+            val methodFoo = usageElement.getMethodByJvmName("foo")
+            assertThat(superTypeHierarchy(methodFoo.returnType)).isEqualTo(
+                """
+                > test.Foo<java.lang.String, java.lang.Integer>
+                  > java.lang.Object
+                  > test.Bar<test.Baz<java.lang.String, java.lang.Number>, java.lang.Integer>
+                    > java.lang.Object
+                    > test.Baz<test.Baz<java.lang.String, java.lang.Number>, java.lang.Integer>
+                      > java.lang.Object
+                """.trimIndent()
+            )
+
+            // Check the supertypes of Foo<Double, Integer>
+            assertThat(superTypeHierarchy(methodFoo.parameters[0].type)).isEqualTo(
+                """
+                > test.Foo<java.lang.Double, java.lang.Integer>
+                  > java.lang.Object
+                  > test.Bar<test.Baz<java.lang.Double, java.lang.Number>, java.lang.Integer>
+                    > java.lang.Object
+                    > test.Baz<test.Baz<java.lang.Double, java.lang.Number>, java.lang.Integer>
+                      > java.lang.Object
+                """.trimIndent()
+            )
+        }
+
+        runProcessorTest(listOf(Source.java(
+            "test.Usage",
+            """
+            package test;
+            interface Usage extends Foo<Long, Integer> {
+                Foo<String, Integer> foo(Foo<Double, Integer> param);
+            }
+            interface Foo<V1, V2 extends Integer> extends Bar<Baz<V1, Number>, V2> {}
+            interface Bar<U1, U2 extends Integer> extends Baz<U1, U2> {}
+            interface Baz<T1, T2 extends Number> {
+                T1 method1();
+                T2 method2();
+            }
+            """.trimIndent()
+        ))) { it.checkType() }
+
+        runProcessorTest(listOf(Source.kotlin(
+            "test.Usage.kt",
+            """
+            package test
+            interface Usage : Foo<Long, Integer> {
+                fun foo(param: Foo<Double, Integer>): Foo<String, Integer>
+            }
+            interface Foo<V1, V2: Integer> : Bar<Baz<V1, Number>, V2> {}
+            interface Bar<U1, U2: Integer> : Baz<U1, U2> {}
+            interface Baz<T1, T2: Number> {
+                fun method1(): T1
+                fun method2(): T2
+            }
+            """.trimIndent()
+        ))) { it.checkType() }
+    }
+
+    @Test
+    fun typeArgumentMissingType() {
+        class TypeArgumentProcessingStep : XProcessingStep {
+            override fun annotations() = setOf("test.Inspect")
+
+            override fun process(
+                env: XProcessingEnv,
+                elementsByAnnotation: Map<String, Set<XElement>>,
+                isLastRound: Boolean
+            ): Set<XElement> {
+                val barElement = env.requireTypeElement("test.Bar")
+                val missingTypeName = if (
+                    env.backend == XProcessingEnv.Backend.KSP ||
+                    // There's a bug in KAPT that doesn't replace NonExistentClass even when
+                    // correctErrorTypes is enabled, so we account for that here.
+                    // https://youtrack.jetbrains.com/issue/KT-34193/Kapt-CorrectErrorTypes-doesnt-work-for-generics
+                    barElement.hasAnnotation(Metadata::class)
+                ) {
+                    ClassName.get("error", "NonExistentClass")
+                } else {
+                    ClassName.get("", "MissingType")
+                }
+                val barType = barElement.type
+                val fooTypeName = ParameterizedTypeName.get(
+                    ClassName.get("test", "Foo"),
+                    missingTypeName
+                )
+
+                val fooType = barType.superTypes.single()
+                assertThat(fooType.typeName).isEqualTo(fooTypeName)
+                assertThat(fooType.isError()).isFalse()
+
+                val typeArgument = fooType.typeArguments.single()
+                assertThat(typeArgument.typeName).isEqualTo(missingTypeName)
+                assertThat(typeArgument.isError()).isTrue()
+
+                return emptySet()
+            }
+        }
+
+        runProcessorTest(
+            sources = listOf(Source.java(
+                "test.Foo",
+                """
+                package test;
+                @Inspect
+                class Bar extends Foo<MissingType> {}
+                class Foo<T> {}
+                @interface Inspect {}
+                """.trimIndent()
+            )),
+            createProcessingStep = { TypeArgumentProcessingStep() }
+        ) { result ->
+            result.hasError()
+            result.hasErrorCount(1)
+            result.hasErrorContaining("cannot find symbol")
+        }
+
+        runProcessorTest(
+            sources = listOf(Source.kotlin(
+                "test.Foo.kt",
+                """
+            package test
+            class Bar : Foo<MissingType>()
+            open class Foo<T>
+            """.trimIndent()
+            )),
+            kotlincArguments = listOf(
+                "-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"
+            ),
+            createProcessingStep = { TypeArgumentProcessingStep() }
+        ) { result ->
+            result.hasError()
+            result.hasErrorCount(1)
+            result.hasErrorContaining("Unresolved reference")
+        }
+    }
+
+    @Test
+    fun wildcardWithMissingType() {
+        class WildcardProcessingStep : XProcessingStep {
+            override fun annotations() = setOf("test.Inspect")
+
+            override fun process(
+                env: XProcessingEnv,
+                elementsByAnnotation: Map<String, Set<XElement>>,
+                isLastRound: Boolean
+            ): Set<XElement> {
+                val missingTypeName = if (env.backend == XProcessingEnv.Backend.KSP) {
+                    ClassName.get("error", "NonExistentClass")
+                } else {
+                    ClassName.get("", "MissingType")
+                }
+                val wildcardTypeName = WildcardTypeName.subtypeOf(missingTypeName)
+                val fooTypeName = ParameterizedTypeName.get(
+                    ClassName.get("test", "Foo"),
+                    wildcardTypeName
+                )
+
+                val fooElement = env.requireTypeElement("test.Foo")
+                val fooType = fooElement.getField("foo").type
+                assertThat(fooType.typeName).isEqualTo(fooTypeName)
+                assertThat(fooType.isError()).isFalse()
+
+                val wildcardType = fooType.typeArguments.single()
+                assertThat(wildcardType.typeName).isEqualTo(wildcardTypeName)
+                assertThat(wildcardType.isError()).isFalse()
+
+                assertThat(wildcardType.extendsBound()).isNotNull()
+                val errorType = wildcardType.extendsBound()!!
+                assertThat(errorType.typeName).isEqualTo(missingTypeName)
+                assertThat(errorType.isError()).isTrue()
+
+                return emptySet()
+            }
+        }
+
+        runProcessorTest(
+            sources = listOf(Source.java(
+                "test.Foo",
+                """
+                package test;
+                @Inspect
+                class Foo<T> {
+                  Foo<? extends MissingType> foo;
+                }
+                @interface Inspect {}
+                """.trimIndent()
+            )),
+            createProcessingStep = { WildcardProcessingStep() }
+        ) { result ->
+            result.hasError()
+            result.hasErrorCount(1)
+            result.hasErrorContaining("cannot find symbol")
+        }
+
+        runProcessorTest(
+            sources = listOf(Source.kotlin(
+                "test.Foo.kt",
+                """
+            package test
+            class Foo<T> {
+              val foo: Foo<out MissingType> = TODO()
+            }
+            """.trimIndent()
+            )),
+            kotlincArguments = listOf(
+                "-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"
+            ),
+            createProcessingStep = { WildcardProcessingStep() }
+        ) { result ->
+            result.hasError()
+            result.hasErrorCount(1)
+            result.hasErrorContaining("Unresolved reference")
+        }
+    }
 }
diff --git a/settings.gradle b/settings.gradle
index 78a99ac..94f37a7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -657,9 +657,13 @@
 includeProject(":glance:glance-appwidget-preview", [BuildType.GLANCE])
 includeProject(":glance:glance-appwidget-proto", [BuildType.GLANCE])
 includeProject(":glance:glance-appwidget:integration-tests:demos", [BuildType.GLANCE])
+includeProject(":glance:glance-appwidget:integration-tests:macrobenchmark", [BuildType.GLANCE])
+includeProject(":glance:glance-appwidget:integration-tests:macrobenchmark-target", [BuildType.GLANCE])
 includeProject(":glance:glance-appwidget:integration-tests:template-demos", [BuildType.GLANCE])
 includeProject(":glance:glance-appwidget:glance-layout-generator", [BuildType.GLANCE])
 includeProject(":glance:glance-preview", [BuildType.GLANCE])
+includeProject(":glance:glance-material", [BuildType.GLANCE])
+includeProject(":glance:glance-material3", [BuildType.GLANCE])
 includeProject(":glance:glance-wear-tiles:integration-tests:demos", [BuildType.GLANCE])
 includeProject(":glance:glance-wear-tiles:integration-tests:template-demos", [BuildType.GLANCE])
 includeProject(":glance:glance-wear-tiles", [BuildType.GLANCE])
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java
index 40b8b2a..5322d2a 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java
@@ -50,7 +50,6 @@
     @NonNull
     public UiObject getChildByDescription(@NonNull UiSelector childPattern, @NonNull String text)
             throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, text);
         if (text != null) {
             int count = getChildCount(childPattern);
             for (int x = 0; x < count; x++) {
@@ -84,7 +83,6 @@
     @NonNull
     public UiObject getChildByInstance(@NonNull UiSelector childPattern, int instance)
             throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, instance);
         UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
                 UiSelector.patternBuilder(childPattern).instance(instance));
         return new UiObject(patternSelector);
@@ -108,7 +106,6 @@
     @NonNull
     public UiObject getChildByText(@NonNull UiSelector childPattern, @NonNull String text)
             throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, text);
         if (text != null) {
             int count = getChildCount(childPattern);
             for (int x = 0; x < count; x++) {
@@ -137,7 +134,6 @@
      * @return the number of matched childPattern under the current {@link UiCollection}
      */
     public int getChildCount(@NonNull UiSelector childPattern) {
-        Tracer.trace(childPattern);
         UiSelector patternSelector =
                 UiSelector.patternBuilder(getSelector(), UiSelector.patternBuilder(childPattern));
         return getQueryController().getPatternCount(patternSelector);
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
index 58e73a7..db7e3d3 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
@@ -37,7 +37,8 @@
  * be reused for different views that match the selector criteria.
  */
 public class UiObject {
-    private static final String LOG_TAG = UiObject.class.getSimpleName();
+    private static final String TAG = UiObject.class.getSimpleName();
+
     /** @deprecated use {@link Configurator#setWaitForSelectorTimeout(long)} */
     @Deprecated
     protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
@@ -86,7 +87,6 @@
      */
     @NonNull
     public final UiSelector getSelector() {
-        Tracer.trace();
         if (mUiSelector == null) {
             throw new IllegalStateException("UiSelector not set");
         }
@@ -125,7 +125,6 @@
      */
     @NonNull
     public UiObject getChild(@NonNull UiSelector selector) throws UiObjectNotFoundException {
-        Tracer.trace(selector);
         return new UiObject(getSelector().childSelector(selector));
     }
 
@@ -139,7 +138,6 @@
      */
     @NonNull
     public UiObject getFromParent(@NonNull UiSelector selector) throws UiObjectNotFoundException {
-        Tracer.trace(selector);
         return new UiObject(getSelector().fromParent(selector));
     }
 
@@ -150,7 +148,6 @@
      * @throws UiObjectNotFoundException
      */
     public int getChildCount() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -200,6 +197,8 @@
     public boolean dragTo(@NonNull UiObject destObj, int steps) throws UiObjectNotFoundException {
         Rect srcRect = getVisibleBounds();
         Rect dstRect = destObj.getVisibleBounds();
+        Log.d(TAG, String.format("Dragging from (%d, %d) to (%d, %d) in %d steps.",
+                srcRect.centerX(), srcRect.centerY(), dstRect.centerX(), dstRect.centerY(), steps));
         return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(),
                 dstRect.centerX(), dstRect.centerY(), steps, true);
     }
@@ -218,6 +217,8 @@
      */
     public boolean dragTo(int destX, int destY, int steps) throws UiObjectNotFoundException {
         Rect srcRect = getVisibleBounds();
+        Log.d(TAG, String.format("Dragging from (%d, %d) to (%d, %d) in %d steps.",
+                srcRect.centerX(), srcRect.centerY(), destX, destY, steps));
         return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(), destX, destY,
                 steps, true);
     }
@@ -238,10 +239,15 @@
      * @throws UiObjectNotFoundException
      */
     public boolean swipeUp(int steps) throws UiObjectNotFoundException {
-        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
-        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
-            return false; // too small to swipe
+        if (rect.height() <= SWIPE_MARGIN_LIMIT * 2) {
+            Log.w(TAG, String.format("Cannot swipe. Object height too small (%d < %d).",
+                    rect.height(), SWIPE_MARGIN_LIMIT * 2));
+            return false;
+        }
+        Log.d(TAG, String.format("Swiping up from (%d, %d) to (%d, %d) in %d steps.",
+                rect.centerX(), rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(),
+                rect.top + SWIPE_MARGIN_LIMIT, steps));
         return getInteractionController().swipe(rect.centerX(),
                 rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
                 steps);
@@ -265,10 +271,15 @@
      * @throws UiObjectNotFoundException
      */
     public boolean swipeDown(int steps) throws UiObjectNotFoundException {
-        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
-        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
-            return false; // too small to swipe
+        if (rect.height() <= SWIPE_MARGIN_LIMIT * 2) {
+            Log.w(TAG, String.format("Cannot swipe. Object height too small (%d < %d).",
+                    rect.height(), SWIPE_MARGIN_LIMIT * 2));
+            return false;
+        }
+        Log.d(TAG, String.format("Swiping down from (%d, %d) to (%d, %d) in %d steps.",
+                rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
+                rect.bottom - SWIPE_MARGIN_LIMIT, steps));
         return getInteractionController().swipe(rect.centerX(),
                 rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
                 rect.bottom - SWIPE_MARGIN_LIMIT, steps);
@@ -292,10 +303,15 @@
      * @throws UiObjectNotFoundException
      */
     public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
-        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
-        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
-            return false; // too small to swipe
+        if (rect.width() <= SWIPE_MARGIN_LIMIT * 2) {
+            Log.w(TAG, String.format("Cannot swipe. Object width too small (%d < %d).",
+                    rect.width(), SWIPE_MARGIN_LIMIT * 2));
+            return false;
+        }
+        Log.d(TAG, String.format("Swiping left from (%d, %d) to (%d, %d) in %d steps.",
+                rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT,
+                rect.centerY(), steps));
         return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
                 rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
     }
@@ -318,10 +334,15 @@
      * @throws UiObjectNotFoundException
      */
     public boolean swipeRight(int steps) throws UiObjectNotFoundException {
-        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
-        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
-            return false; // too small to swipe
+        if (rect.width() <= SWIPE_MARGIN_LIMIT * 2) {
+            Log.w(TAG, String.format("Cannot swipe. Object width too small (%d < %d).",
+                    rect.width(), SWIPE_MARGIN_LIMIT * 2));
+            return false;
+        }
+        Log.d(TAG, String.format("Swiping right from (%d, %d) to (%d, %d) in %d steps.",
+                rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT,
+                rect.centerY(), steps));
         return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
                 rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
     }
@@ -386,12 +407,12 @@
      * @throws UiObjectNotFoundException
      */
     public boolean click() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG, String.format("Clicking on (%d, %d).", rect.centerX(), rect.centerY()));
         return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(),
                 mConfig.getActionAcknowledgmentTimeout());
     }
@@ -405,7 +426,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
-        Tracer.trace();
         return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
     }
 
@@ -426,12 +446,14 @@
      * @throws UiObjectNotFoundException
      */
     public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
-        Tracer.trace(timeout);
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG,
+                String.format("Clicking on (%d, %d) and waiting %dms for new window.",
+                        rect.centerX(), rect.centerY(), timeout));
         return getInteractionController().clickAndWaitForNewWindow(rect.centerX(), rect.centerY(),
                 timeout);
     }
@@ -443,12 +465,12 @@
      * @throws UiObjectNotFoundException
      */
     public boolean clickTopLeft() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG, String.format("Clicking on (%d, %d).", rect.left + 5, rect.top + 5));
         return getInteractionController().clickAndSync(rect.left + 5, rect.top + 5,
                 mConfig.getActionAcknowledgmentTimeout());
     }
@@ -460,12 +482,12 @@
      * @throws UiObjectNotFoundException
      */
     public boolean longClickBottomRight() throws UiObjectNotFoundException  {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG, String.format("Long-clicking on (%d, %d).", rect.right - 5, rect.bottom - 5));
         return getInteractionController().longTapAndSync(rect.right - 5, rect.bottom - 5,
                 mConfig.getActionAcknowledgmentTimeout());
     }
@@ -477,12 +499,12 @@
      * @throws UiObjectNotFoundException
      */
     public boolean clickBottomRight() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG, String.format("Clicking on (%d, %d).", rect.right - 5, rect.bottom - 5));
         return getInteractionController().clickAndSync(rect.right - 5, rect.bottom - 5,
                 mConfig.getActionAcknowledgmentTimeout());
     }
@@ -494,12 +516,12 @@
      * @throws UiObjectNotFoundException
      */
     public boolean longClick() throws UiObjectNotFoundException  {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG, String.format("Long-clicking on (%d, %d).", rect.centerX(), rect.centerY()));
         return getInteractionController().longTapAndSync(rect.centerX(), rect.centerY(),
                 mConfig.getActionAcknowledgmentTimeout());
     }
@@ -511,12 +533,12 @@
      * @throws UiObjectNotFoundException
      */
     public boolean longClickTopLeft() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
         Rect rect = getVisibleBounds(node);
+        Log.d(TAG, String.format("Long-clicking on (%d, %d).", rect.left + 5, rect.top + 5));
         return getInteractionController().longTapAndSync(rect.left + 5, rect.top + 5,
                 mConfig.getActionAcknowledgmentTimeout());
     }
@@ -529,14 +551,11 @@
      */
     @NonNull
     public String getText() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
-        String retVal = safeStringReturn(node.getText());
-        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
-        return retVal;
+        return safeStringReturn(node.getText());
     }
 
     /**
@@ -547,14 +566,11 @@
      */
     @NonNull
     public String getClassName() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
         }
-        String retVal = safeStringReturn(node.getClassName());
-        Log.d(LOG_TAG, String.format("getClassName() = %s", retVal));
-        return retVal;
+        return safeStringReturn(node.getClassName());
     }
 
     /**
@@ -565,7 +581,6 @@
      */
     @NonNull
     public String getContentDescription() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -582,12 +597,12 @@
         if (text == null) {
             text = "";
         }
-        Tracer.trace(text);
         // long click left + center
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if (node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
         }
+        Log.d(TAG, String.format("Setting text to '%s'.", text));
         Rect rect = getVisibleBounds(node);
         getInteractionController().longTapNoSync(rect.left + 20, rect.centerY());
         // check if the edit menu is open
@@ -631,7 +646,6 @@
         if (text == null) {
             text = "";
         }
-        Tracer.trace(text);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
             // ACTION_SET_TEXT is added in API 21.
             AccessibilityNodeInfo node = findAccessibilityNodeInfo(
@@ -639,11 +653,13 @@
             if (node == null) {
                 throw new UiObjectNotFoundException(getSelector().toString());
             }
+            Log.d(TAG, String.format("Setting text to '%s'.", text));
             Bundle args = new Bundle();
             args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
             return node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
         } else {
             clearTextField();
+            Log.d(TAG, String.format("Setting text to '%s'.", text));
             return getInteractionController().sendText(text);
         }
     }
@@ -659,7 +675,6 @@
      * @throws UiObjectNotFoundException
      */
     public void clearTextField() throws UiObjectNotFoundException {
-        Tracer.trace();
         // long click left + center
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
@@ -671,6 +686,7 @@
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                 setText("");
             } else {
+                Log.d(TAG, "Setting text to ''.");
                 Bundle selectionArgs = new Bundle();
                 // select all of the existing text
                 selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
@@ -678,11 +694,11 @@
                         text.length());
                 boolean ret = node.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
                 if (!ret) {
-                    Log.w(LOG_TAG, "ACTION_FOCUS on text field failed.");
+                    Log.w(TAG, "ACTION_FOCUS on text field failed.");
                 }
                 ret = node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, selectionArgs);
                 if (!ret) {
-                    Log.w(LOG_TAG, "ACTION_SET_SELECTION on text field failed.");
+                    Log.w(TAG, "ACTION_SET_SELECTION on text field failed.");
                 }
                 // now delete all
                 getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
@@ -696,7 +712,6 @@
      * @return true if it is else false
      */
     public boolean isChecked() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -711,7 +726,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isSelected() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -726,7 +740,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isCheckable() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -741,7 +754,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isEnabled() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -756,7 +768,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isClickable() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -771,7 +782,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isFocused() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -786,7 +796,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isFocusable() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -801,7 +810,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isScrollable() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -816,7 +824,6 @@
      * @throws UiObjectNotFoundException
      */
     public boolean isLongClickable() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -832,7 +839,6 @@
      */
     @NonNull
     public String getPackageName() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -852,7 +858,6 @@
      */
     @NonNull
     public Rect getVisibleBounds() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -868,7 +873,6 @@
      */
     @NonNull
     public Rect getBounds() throws UiObjectNotFoundException {
-        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
         if(node == null) {
             throw new UiObjectNotFoundException(mUiSelector.toString());
@@ -890,7 +894,7 @@
      * @return true if the view is displayed, else false if timeout elapsed while waiting
      */
     public boolean waitForExists(long timeout) {
-        Tracer.trace(timeout);
+        Log.d(TAG, String.format("Waiting %dms for %s.", timeout, mUiSelector));
         if(findAccessibilityNodeInfo(timeout) != null) {
             return true;
         }
@@ -915,7 +919,7 @@
      * but a matching element is still found.
      */
     public boolean waitUntilGone(long timeout) {
-        Tracer.trace(timeout);
+        Log.d(TAG, String.format("Waiting %dms for %s to be gone.", timeout, mUiSelector));
         long startMills = SystemClock.uptimeMillis();
         long currentMills = 0;
         while (currentMills <= timeout) {
@@ -939,7 +943,6 @@
      * @return true if the view represented by this UiObject does exist
      */
     public boolean exists() {
-        Tracer.trace();
         return waitForExists(0);
     }
 
@@ -1118,6 +1121,24 @@
      *         <code>false</code> otherwise
      */
     public boolean performMultiPointerGesture(@NonNull PointerCoords[]... touches) {
+        Log.d(TAG, String.format("Performing multi-point gesture %s", touchesToString(touches)));
         return getInteractionController().performMultiPointerGesture(touches);
     }
+
+    private static String touchesToString(@NonNull PointerCoords[]... touches) {
+        StringBuilder result = new StringBuilder();
+        result.append("[");
+        for (int i = 0; i < touches.length; i++) {
+            result.append("[");
+            for (int j = 0; j < touches[i].length; j++) {
+                PointerCoords point = touches[i][j];
+                result.append(String.format("(%f, %f)", point.x, point.y));
+                if (j + 1 < touches[i].length) result.append(", ");
+            }
+            result.append("]");
+            if (i + 1 < touches.length) result.append(", ");
+        }
+        result.append("]");
+        return result.toString();
+    }
 }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
index c60e8c0..8dcfbf5 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
@@ -28,7 +28,7 @@
  * horizontally or vertically scrollable controls.
  */
 public class UiScrollable extends UiCollection {
-    private static final String LOG_TAG = UiScrollable.class.getSimpleName();
+    private static final String TAG = UiScrollable.class.getSimpleName();
 
     // More steps slows the swipe and prevents contents from being flung too far
     private static final int SCROLL_STEPS = 55;
@@ -64,7 +64,6 @@
      */
     @NonNull
     public UiScrollable setAsVerticalList() {
-        Tracer.trace();
         mIsVerticalList = true;
         return this;
     }
@@ -75,7 +74,6 @@
      */
     @NonNull
     public UiScrollable setAsHorizontalList() {
-        Tracer.trace();
         mIsVerticalList = false;
         return this;
     }
@@ -115,7 +113,6 @@
     public UiObject getChildByDescription(
             @NonNull UiSelector childPattern, @NonNull String text)
             throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, text);
         return getChildByDescription(childPattern, text, true);
     }
 
@@ -137,7 +134,6 @@
     @NonNull
     public UiObject getChildByDescription(@NonNull UiSelector childPattern, @NonNull String text,
             boolean allowScrollSearch) throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, text, allowScrollSearch);
         if (text != null) {
             if (allowScrollSearch) {
                 scrollIntoView(new UiSelector().descriptionContains(text));
@@ -161,7 +157,6 @@
     @Override
     public UiObject getChildByInstance(@NonNull UiSelector childPattern, int instance)
             throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, instance);
         UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
                 UiSelector.patternBuilder(childPattern).instance(instance));
         return new UiObject(patternSelector);
@@ -186,7 +181,6 @@
     @Override
     public UiObject getChildByText(@NonNull UiSelector childPattern, @NonNull String text)
             throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, text);
         return getChildByText(childPattern, text, true);
     }
 
@@ -208,7 +202,6 @@
     public UiObject getChildByText(@NonNull UiSelector childPattern,
             @NonNull String text,
             boolean allowScrollSearch) throws UiObjectNotFoundException {
-        Tracer.trace(childPattern, text, allowScrollSearch);
         if (text != null) {
             if (allowScrollSearch) {
                 scrollIntoView(new UiSelector().text(text));
@@ -229,7 +222,6 @@
      */
     public boolean scrollDescriptionIntoView(@NonNull String text)
             throws UiObjectNotFoundException {
-        Tracer.trace(text);
         return scrollIntoView(new UiSelector().description(text));
     }
 
@@ -241,7 +233,6 @@
      * @return true if the item was found and now is in view else false
      */
     public boolean scrollIntoView(@NonNull UiObject obj) throws UiObjectNotFoundException {
-        Tracer.trace(obj.getSelector());
         return scrollIntoView(obj.getSelector());
     }
 
@@ -255,7 +246,7 @@
      * @return true if the item was found and now is in view; else, false
      */
     public boolean scrollIntoView(@NonNull UiSelector selector) throws UiObjectNotFoundException {
-        Tracer.trace(selector);
+        Log.d(TAG, String.format("Scrolling %s into view.", selector));
         // if we happen to be on top of the text we want then return here
         UiSelector childSelector = getSelector().childSelector(selector);
         if (exists(childSelector)) {
@@ -292,6 +283,7 @@
      */
     public boolean ensureFullyVisible(@NonNull UiObject childObject)
             throws UiObjectNotFoundException {
+        Log.d(TAG, String.format("Ensuring %s is fully visible.", childObject.getSelector()));
         Rect actual = childObject.getBounds();
         Rect visible = childObject.getVisibleBounds();
         if (visible.width() * visible.height() == actual.width() * actual.height()) {
@@ -332,7 +324,6 @@
      * @return true if item is found; else, false
      */
     public boolean scrollTextIntoView(@NonNull String text) throws UiObjectNotFoundException {
-        Tracer.trace(text);
         return scrollIntoView(new UiSelector().text(text));
     }
 
@@ -347,7 +338,6 @@
      */
     @NonNull
     public UiScrollable setMaxSearchSwipes(int swipes) {
-        Tracer.trace(swipes);
         mMaxSearchSwipes = swipes;
         return this;
     }
@@ -361,7 +351,6 @@
      * @return max the number of search swipes to perform until giving up
      */
     public int getMaxSearchSwipes() {
-        Tracer.trace();
         return mMaxSearchSwipes;
     }
 
@@ -376,7 +365,6 @@
      * @return true if scrolled, false if can't scroll anymore
      */
     public boolean flingForward() throws UiObjectNotFoundException {
-        Tracer.trace();
         return scrollForward(FLING_STEPS);
     }
 
@@ -391,7 +379,6 @@
      * @return true if scrolled, false if can't scroll anymore
      */
     public boolean scrollForward() throws UiObjectNotFoundException {
-        Tracer.trace();
         return scrollForward(SCROLL_STEPS);
     }
 
@@ -406,8 +393,6 @@
      * @return true if scrolled, false if can't scroll anymore
      */
     public boolean scrollForward(int steps) throws UiObjectNotFoundException {
-        Tracer.trace(steps);
-        Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -438,6 +423,8 @@
             upX = rect.left + swipeAreaAdjust;
             upY = rect.centerY();
         }
+        Log.d(TAG, String.format("Scrolling forward from (%d, %d) to (%d, %d) in %d steps.", downX,
+                downY, upX, upY, steps));
         return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
     }
 
@@ -452,7 +439,6 @@
      * @return true if scrolled, and false if can't scroll anymore
      */
     public boolean flingBackward() throws UiObjectNotFoundException {
-        Tracer.trace();
         return scrollBackward(FLING_STEPS);
     }
 
@@ -467,7 +453,6 @@
      * @return true if scrolled, and false if can't scroll anymore
      */
     public boolean scrollBackward() throws UiObjectNotFoundException {
-        Tracer.trace();
         return scrollBackward(SCROLL_STEPS);
     }
 
@@ -482,8 +467,6 @@
      * @return true if scrolled, false if can't scroll anymore
      */
     public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
-        Tracer.trace(steps);
-        Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if (node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -500,7 +483,6 @@
         // set otherwise by setAsHorizontalContainer()
         if(mIsVerticalList) {
             int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
-            Log.d(LOG_TAG, "scrollToBeginning() using vertical scroll");
             // scroll vertically: swipe up -> down
             downX = rect.centerX();
             downY = rect.top + swipeAreaAdjust;
@@ -508,7 +490,6 @@
             upY = rect.bottom - swipeAreaAdjust;
         } else {
             int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
-            Log.d(LOG_TAG, "scrollToBeginning() using hotizontal scroll");
             // scroll horizontally: swipe left -> right
             // TODO: Assuming device is not in right to left language
             downX = rect.left + swipeAreaAdjust;
@@ -516,6 +497,8 @@
             upX = rect.right - swipeAreaAdjust;
             upY = rect.centerY();
         }
+        Log.d(TAG, String.format("Scrolling backward from (%d, %d) to (%d, %d) in %d steps.", downX,
+                downY, upX, upY, steps));
         return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
     }
 
@@ -529,8 +512,6 @@
      * @return true on scrolled else false
      */
     public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
-        Tracer.trace(maxSwipes, steps);
-        Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
         // protect against potential hanging and return after preset attempts
         for(int x = 0; x < maxSwipes; x++) {
             if(!scrollBackward(steps)) {
@@ -550,7 +531,6 @@
      * @return true on scrolled else false
      */
     public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
-        Tracer.trace(maxSwipes);
         return scrollToBeginning(maxSwipes, SCROLL_STEPS);
     }
 
@@ -564,7 +544,6 @@
      * @return true on scrolled else false
      */
     public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
-        Tracer.trace(maxSwipes);
         return scrollToBeginning(maxSwipes, FLING_STEPS);
     }
 
@@ -578,7 +557,6 @@
      * @return true on scrolled else false
      */
     public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
-        Tracer.trace(maxSwipes, steps);
         // protect against potential hanging and return after preset attempts
         for(int x = 0; x < maxSwipes; x++) {
             if(!scrollForward(steps)) {
@@ -598,7 +576,6 @@
      * @return true on scrolled, else false
      */
     public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
-        Tracer.trace(maxSwipes);
         return scrollToEnd(maxSwipes, SCROLL_STEPS);
     }
 
@@ -612,7 +589,6 @@
      * @return true on scrolled, else false
      */
     public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
-        Tracer.trace(maxSwipes);
         return scrollToEnd(maxSwipes, FLING_STEPS);
     }
 
@@ -627,7 +603,6 @@
      * @return a value between 0 and 1
      */
     public double getSwipeDeadZonePercentage() {
-        Tracer.trace();
         return mSwipeDeadZonePercentage;
     }
 
@@ -645,7 +620,6 @@
      */
     @NonNull
     public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
-        Tracer.trace(swipeDeadZonePercentage);
         mSwipeDeadZonePercentage = swipeDeadZonePercentage;
         return this;
     }
diff --git a/tracing/tracing-perfetto/build.gradle b/tracing/tracing-perfetto/build.gradle
index ca9af2c..f69a48a 100644
--- a/tracing/tracing-perfetto/build.gradle
+++ b/tracing/tracing-perfetto/build.gradle
@@ -67,5 +67,8 @@
 }
 
 android {
+    buildTypes.all {
+        consumerProguardFiles "proguard-rules.pro"
+    }
     namespace "androidx.tracing.perfetto"
 }
diff --git a/tracing/tracing-perfetto/proguard-rules.pro b/tracing/tracing-perfetto/proguard-rules.pro
new file mode 100644
index 0000000..526c4f3
--- /dev/null
+++ b/tracing/tracing-perfetto/proguard-rules.pro
@@ -0,0 +1,7 @@
+# Preserve class methods (that we explicitly register in native code), if the class is preserved
+-keepclassmembers class androidx.tracing.perfetto.jni.PerfettoNative {
+    java.lang.String nativeVersion();
+    void nativeRegisterWithPerfetto();
+    void nativeTraceEventBegin(int, java.lang.String);
+    void nativeTraceEventEnd();
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToDismissBoxSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToDismissBoxSample.kt
index 65e66eb..95c8021 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToDismissBoxSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToDismissBoxSample.kt
@@ -153,6 +153,8 @@
 ) {
     val state = rememberSwipeToDismissBoxState()
 
+    // When using Modifier.edgeSwipeToDismiss, it is required that the element on which the
+    // modifier applies exists within a SwipeToDismissBox which shares the same state.
     SwipeToDismissBox(
         state = state,
         >
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
index 7264351..7b38d00 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
@@ -471,6 +471,9 @@
  * Currently Edge swipe, like swipe to dismiss, is only supported on the left part of the viewport
  * regardless of layout direction as content is swiped away from left to right.
  *
+ * Requires that the element to which this modifier is applied exists within a
+ * SwipeToDismissBox which is using the same [SwipeToDismissBoxState] instance.
+ *
  * Example of a modifier usage with SwipeToDismiss
  * @sample androidx.wear.compose.material.samples.EdgeSwipeForSwipeToDismiss
  *
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemo.kt
index e738dff..afda929 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemo.kt
@@ -101,6 +101,9 @@
     val colors = listOf(Color.Blue, Color.Red, Color.Green, Color.Cyan, Color.Magenta)
     Box(modifier = Modifier.fillMaxSize()) {
         LazyRow(
+            // When using Modifier.edgeSwipeToDismiss, it is required that the element on which the
+            // modifier applies exists within a SwipeToDismissBox which shares the same state.
+            // Here, we share the swipeToDismissBoxState used by DemoApp's SwipeToDismissBox.
             modifier = Modifier.border(4.dp, Color.DarkGray)
                 .fillMaxSize()
                 .edgeSwipeToDismiss(swipeToDismissBoxState),
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
new file mode 100644
index 0000000..7bdb9f6
--- /dev/null
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2022 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.watchface.client.test
+
+import android.annotation.SuppressLint
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.Rect
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import androidx.test.screenshot.assertAgainstGolden
+import androidx.wear.watchface.ComplicationSlotBoundsType
+import androidx.wear.watchface.DrawMode
+import androidx.wear.watchface.RenderParameters
+import androidx.wear.watchface.client.DeviceConfig
+import androidx.wear.watchface.client.HeadlessWatchFaceClient
+import androidx.wear.watchface.client.WatchFaceControlClient
+import androidx.wear.watchface.client.test.TestServicesHelpers.componentOf
+import androidx.wear.watchface.client.test.TestServicesHelpers.createTestComplications
+import androidx.wear.watchface.complications.SystemDataSources
+import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.control.WatchFaceControlService
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
+import androidx.wear.watchface.samples.ExampleOpenGLBackgroundInitWatchFaceService
+import androidx.wear.watchface.style.WatchFaceLayer
+import com.google.common.truth.Truth
+import java.time.Instant
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+abstract class HeadlessWatchFaceClientTestBase {
+    protected val context: Context = ApplicationProvider.getApplicationContext()
+    protected val service = runBlocking {
+        WatchFaceControlClient.createWatchFaceControlClientImpl(
+            context,
+            Intent(context, WatchFaceControlTestService::class.java).apply {
+                action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+            }
+        )
+    }
+
+    protected fun createHeadlessWatchFaceClient(
+        componentName: ComponentName = exampleCanvasAnalogWatchFaceComponentName
+    ): HeadlessWatchFaceClient {
+        return service.createHeadlessWatchFaceClient(
+            "id",
+            componentName,
+            deviceConfig,
+            400,
+            400
+        )!!
+    }
+
+    protected val exampleCanvasAnalogWatchFaceComponentName =
+        componentOf<ExampleCanvasAnalogWatchFaceService>()
+
+    protected val exampleOpenGLWatchFaceComponentName =
+        componentOf<ExampleOpenGLBackgroundInitWatchFaceService>()
+
+    protected val deviceConfig = DeviceConfig(
+        hasLowBitAmbient = false,
+        hasBurnInProtection = false,
+        analogPreviewReferenceTimeMillis = 0,
+        digitalPreviewReferenceTimeMillis = 0
+    )
+}
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+class HeadlessWatchFaceClientTest : HeadlessWatchFaceClientTestBase() {
+    @Suppress("DEPRECATION", "NewApi") // defaultDataSourceType
+    @Test
+    fun headlessComplicationDetails() {
+        val headlessInstance = createHeadlessWatchFaceClient()
+
+        Truth.assertThat(headlessInstance.complicationSlotsState.size).isEqualTo(2)
+
+        val leftComplicationDetails = headlessInstance.complicationSlotsState[
+            EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
+        ]!!
+        Truth.assertThat(leftComplicationDetails.bounds).isEqualTo(Rect(80, 160, 160, 240))
+        Truth.assertThat(leftComplicationDetails.boundsType)
+            .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
+        Truth.assertThat(
+            leftComplicationDetails.defaultDataSourcePolicy.systemDataSourceFallback
+        ).isEqualTo(
+            SystemDataSources.DATA_SOURCE_DAY_OF_WEEK
+        )
+        Truth.assertThat(leftComplicationDetails.defaultDataSourceType).isEqualTo(
+            ComplicationType.SHORT_TEXT
+        )
+        Truth.assertThat(leftComplicationDetails.supportedTypes).containsExactly(
+            ComplicationType.RANGED_VALUE,
+            ComplicationType.GOAL_PROGRESS,
+            ComplicationType.WEIGHTED_ELEMENTS,
+            ComplicationType.LONG_TEXT,
+            ComplicationType.SHORT_TEXT,
+            ComplicationType.MONOCHROMATIC_IMAGE,
+            ComplicationType.SMALL_IMAGE
+        )
+        Assert.assertTrue(leftComplicationDetails.isEnabled)
+
+        val rightComplicationDetails = headlessInstance.complicationSlotsState[
+            EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
+        ]!!
+        Truth.assertThat(rightComplicationDetails.bounds).isEqualTo(Rect(240, 160, 320, 240))
+        Truth.assertThat(rightComplicationDetails.boundsType)
+            .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
+        Truth.assertThat(
+            rightComplicationDetails.defaultDataSourcePolicy.systemDataSourceFallback
+        ).isEqualTo(
+            SystemDataSources.DATA_SOURCE_STEP_COUNT
+        )
+        Truth.assertThat(rightComplicationDetails.defaultDataSourceType).isEqualTo(
+            ComplicationType.SHORT_TEXT
+        )
+        Truth.assertThat(rightComplicationDetails.supportedTypes).containsExactly(
+            ComplicationType.RANGED_VALUE,
+            ComplicationType.GOAL_PROGRESS,
+            ComplicationType.WEIGHTED_ELEMENTS,
+            ComplicationType.LONG_TEXT,
+            ComplicationType.SHORT_TEXT,
+            ComplicationType.MONOCHROMATIC_IMAGE,
+            ComplicationType.SMALL_IMAGE
+        )
+
+        Truth.assertThat(rightComplicationDetails.isEnabled).isTrue()
+
+        headlessInstance.close()
+    }
+
+    @Test
+    @Suppress("Deprecation") // userStyleSettings
+    fun headlessUserStyleSchema() {
+        val headlessInstance = createHeadlessWatchFaceClient()
+
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings.size).isEqualTo(5)
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings[0].id.value).isEqualTo(
+            "color_style_setting"
+        )
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings[1].id.value).isEqualTo(
+            "draw_hour_pips_style_setting"
+        )
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings[2].id.value).isEqualTo(
+            "watch_hand_length_style_setting"
+        )
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings[3].id.value).isEqualTo(
+            "complications_style_setting"
+        )
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings[4].id.value).isEqualTo(
+            "hours_draw_freq_style_setting"
+        )
+
+        headlessInstance.close()
+    }
+
+    @Test
+    fun headlessUserStyleFlavors() {
+        val headlessInstance = createHeadlessWatchFaceClient()
+
+        Truth.assertThat(headlessInstance.getUserStyleFlavors().flavors.size).isEqualTo(1)
+        val flavorA = headlessInstance.getUserStyleFlavors().flavors[0]
+        Truth.assertThat(flavorA.id).isEqualTo("exampleFlavor")
+        Truth.assertThat(flavorA.style.userStyleMap.containsKey("color_style_setting"))
+        Truth.assertThat(flavorA.style.userStyleMap.containsKey("watch_hand_length_style_setting"))
+        Truth.assertThat(flavorA.complications
+            .containsKey(EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID))
+        Truth.assertThat(flavorA.complications
+            .containsKey(EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID))
+
+        headlessInstance.close()
+    }
+
+    @Test
+    @Suppress("Deprecation") // userStyleSettings
+    fun headlessToBundleAndCreateFromBundle() {
+        val headlessInstance = HeadlessWatchFaceClient.createFromBundle(
+            service.createHeadlessWatchFaceClient(
+                "id",
+                exampleCanvasAnalogWatchFaceComponentName,
+                deviceConfig,
+                400,
+                400
+            )!!.toBundle()
+        )
+
+        Truth.assertThat(headlessInstance.userStyleSchema.userStyleSettings.size).isEqualTo(5)
+    }
+
+    @Test
+    fun computeUserStyleSchemaDigestHash() {
+        val headlessInstance1 = createHeadlessWatchFaceClient(
+            exampleCanvasAnalogWatchFaceComponentName
+        )
+
+        val headlessInstance2 = createHeadlessWatchFaceClient(
+            exampleOpenGLWatchFaceComponentName
+        )
+
+        Truth.assertThat(headlessInstance1.getUserStyleSchemaDigestHash()).isNotEqualTo(
+            headlessInstance2.getUserStyleSchemaDigestHash()
+        )
+    }
+
+    @Test
+    fun headlessLifeCycle() {
+        val headlessInstance = createHeadlessWatchFaceClient(
+            componentOf<TestLifeCycleWatchFaceService>()
+        )
+
+        // Blocks until the headless instance has been fully constructed.
+        headlessInstance.previewReferenceInstant
+        headlessInstance.close()
+
+        Truth.assertThat(TestLifeCycleWatchFaceService.lifeCycleEvents).containsExactly(
+            "WatchFaceService.onCreate",
+            "Renderer.constructed",
+            "Renderer.onDestroy",
+            "WatchFaceService.onDestroy"
+        )
+    }
+}
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+class HeadlessWatchFaceClientScreenshotTest : HeadlessWatchFaceClientTestBase() {
+    @get:Rule
+    val screenshotRule: AndroidXScreenshotTestRule =
+        AndroidXScreenshotTestRule("wear/wear-watchface-client")
+
+    private val complications = createTestComplications(context)
+
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun headlessScreenshot() {
+        val headlessInstance = createHeadlessWatchFaceClient()
+
+        val bitmap = headlessInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
+            ),
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
+        )
+
+        bitmap.assertAgainstGolden(screenshotRule, "headlessScreenshot")
+
+        headlessInstance.close()
+    }
+
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun yellowComplicationHighlights() {
+        val headlessInstance = createHeadlessWatchFaceClient()
+
+        val bitmap = headlessInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                RenderParameters.HighlightLayer(
+                    RenderParameters.HighlightedElement.AllComplicationSlots,
+                    Color.YELLOW,
+                    Color.argb(128, 0, 0, 0) // Darken everything else.
+                )
+            ),
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
+        )
+
+        bitmap.assertAgainstGolden(screenshotRule, "yellowComplicationHighlights")
+
+        headlessInstance.close()
+    }
+
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun highlightOnlyLayer() {
+        val headlessInstance = createHeadlessWatchFaceClient()
+
+        val bitmap = headlessInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                emptySet(),
+                RenderParameters.HighlightLayer(
+                    RenderParameters.HighlightedElement.AllComplicationSlots,
+                    Color.YELLOW,
+                    Color.argb(128, 0, 0, 0) // Darken everything else.
+                )
+            ),
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
+        )
+
+        bitmap.assertAgainstGolden(screenshotRule, "highlightOnlyLayer")
+
+        headlessInstance.close()
+    }
+}
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
new file mode 100644
index 0000000..0d06e96
--- /dev/null
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
@@ -0,0 +1,758 @@
+/*
+ * Copyright 2022 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.watchface.client.test
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.RectF
+import android.view.SurfaceHolder
+import androidx.wear.watchface.BoundingArc
+import androidx.wear.watchface.CanvasComplication
+import androidx.wear.watchface.CanvasType
+import androidx.wear.watchface.ComplicationSlot
+import androidx.wear.watchface.ComplicationSlotsManager
+import androidx.wear.watchface.RenderParameters
+import androidx.wear.watchface.Renderer
+import androidx.wear.watchface.WatchFace
+import androidx.wear.watchface.WatchFaceService
+import androidx.wear.watchface.WatchFaceType
+import androidx.wear.watchface.WatchState
+import androidx.wear.watchface.complications.ComplicationSlotBounds
+import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
+import androidx.wear.watchface.complications.SystemDataSources
+import androidx.wear.watchface.complications.data.ComplicationData
+import androidx.wear.watchface.complications.data.ComplicationExperimental
+import androidx.wear.watchface.complications.data.ComplicationText
+import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.data.NoDataComplicationData
+import androidx.wear.watchface.complications.data.PlainComplicationText
+import androidx.wear.watchface.complications.data.ShortTextComplicationData
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.COMPLICATIONS_STYLE_SETTING
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.LEFT_COMPLICATION
+import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.NO_COMPLICATIONS
+import androidx.wear.watchface.samples.ExampleOpenGLBackgroundInitWatchFaceService
+import androidx.wear.watchface.samples.R
+import androidx.wear.watchface.style.CurrentUserStyleRepository
+import androidx.wear.watchface.style.UserStyleSchema
+import androidx.wear.watchface.style.UserStyleSetting
+import androidx.wear.watchface.style.WatchFaceLayer
+import java.time.ZoneId
+import java.time.ZonedDateTime
+import java.util.concurrent.CountDownLatch
+import kotlinx.coroutines.CompletableDeferred
+
+internal class TestLifeCycleWatchFaceService : WatchFaceService() {
+    companion object {
+        val lifeCycleEvents = ArrayList<String>()
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        lifeCycleEvents.add("WatchFaceService.onCreate")
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        lifeCycleEvents.add("WatchFaceService.onDestroy")
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.DIGITAL,
+        @Suppress("deprecation")
+        object : Renderer.GlesRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            16
+        ) {
+            init {
+                lifeCycleEvents.add("Renderer.constructed")
+            }
+
+            override fun onDestroy() {
+                super.onDestroy()
+                lifeCycleEvents.add("Renderer.onDestroy")
+            }
+
+            override fun render(zonedDateTime: ZonedDateTime) {}
+
+            override fun renderHighlightLayer(zonedDateTime: ZonedDateTime) {}
+        }
+    )
+}
+
+internal class TestExampleCanvasAnalogWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder
+) : ExampleCanvasAnalogWatchFaceService() {
+    internal lateinit var watchFace: WatchFace
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): WatchFace {
+        watchFace = super.createWatchFace(
+            surfaceHolder,
+            watchState,
+            complicationSlotsManager,
+            currentUserStyleRepository
+        )
+        return watchFace
+    }
+}
+
+internal class TestExampleOpenGLBackgroundInitWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder
+) : ExampleOpenGLBackgroundInitWatchFaceService() {
+    internal lateinit var watchFace: WatchFace
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): WatchFace {
+        watchFace = super.createWatchFace(
+            surfaceHolder,
+            watchState,
+            complicationSlotsManager,
+            currentUserStyleRepository
+        )
+        return watchFace
+    }
+}
+
+internal open class TestCrashingWatchFaceService : WatchFaceService() {
+
+    companion object {
+        const val COMPLICATION_ID = 123
+    }
+
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): ComplicationSlotsManager {
+        return ComplicationSlotsManager(
+            listOf(
+                ComplicationSlot.createRoundRectComplicationSlotBuilder(
+                    COMPLICATION_ID,
+                    { _, _ -> throw Exception("Deliberately crashing") },
+                    listOf(ComplicationType.LONG_TEXT),
+                    DefaultComplicationDataSourcePolicy(
+                        SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET,
+                        ComplicationType.LONG_TEXT
+                    ),
+                    ComplicationSlotBounds(RectF(0.1f, 0.1f, 0.4f, 0.4f))
+                ).build()
+            ),
+            currentUserStyleRepository
+        )
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): WatchFace {
+        throw Exception("Deliberately crashing")
+    }
+}
+
+internal class TestWatchfaceOverlayStyleWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder,
+    private var watchFaceOverlayStyle: WatchFace.OverlayStyle
+) : WatchFaceService() {
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.DIGITAL,
+        @Suppress("deprecation")
+        object : Renderer.CanvasRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            CanvasType.HARDWARE,
+            16
+        ) {
+            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
+                // Actually rendering something isn't required.
+            }
+
+            override fun renderHighlightLayer(
+                canvas: Canvas,
+                bounds: Rect,
+                zonedDateTime: ZonedDateTime
+            ) {
+                // Actually rendering something isn't required.
+            }
+        }
+    ).setOverlayStyle(watchFaceOverlayStyle)
+}
+
+internal class TestAsyncCanvasRenderInitWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder,
+    private var initCompletableDeferred: CompletableDeferred<Unit>
+) : WatchFaceService() {
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.DIGITAL,
+        @Suppress("deprecation")
+        object : Renderer.CanvasRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            CanvasType.HARDWARE,
+            16
+        ) {
+            override suspend fun init() {
+                initCompletableDeferred.await()
+            }
+
+            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
+                // Actually rendering something isn't required.
+            }
+
+            override fun renderHighlightLayer(
+                canvas: Canvas,
+                bounds: Rect,
+                zonedDateTime: ZonedDateTime
+            ) {
+                TODO("Not yet implemented")
+            }
+        }
+    )
+
+    override fun getSystemTimeProvider() = object : SystemTimeProvider {
+        override fun getSystemTimeMillis() = 123456789L
+
+        override fun getSystemTimeZoneId() = ZoneId.of("UTC")
+    }
+}
+
+internal class TestAsyncGlesRenderInitWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder,
+    private var onUiThreadGlSurfaceCreatedCompletableDeferred: CompletableDeferred<Unit>,
+    private var onBackgroundThreadGlContextCreatedCompletableDeferred: CompletableDeferred<Unit>
+) : WatchFaceService() {
+    internal lateinit var watchFace: WatchFace
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.DIGITAL,
+        @Suppress("deprecation")
+        object : Renderer.GlesRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            16
+        ) {
+            override suspend fun onUiThreadGlSurfaceCreated(width: Int, height: Int) {
+                onUiThreadGlSurfaceCreatedCompletableDeferred.await()
+            }
+
+            override suspend fun onBackgroundThreadGlContextCreated() {
+                onBackgroundThreadGlContextCreatedCompletableDeferred.await()
+            }
+
+            override fun render(zonedDateTime: ZonedDateTime) {
+                // GLES rendering is complicated and not strictly necessary for our test.
+            }
+
+            override fun renderHighlightLayer(zonedDateTime: ZonedDateTime) {
+                TODO("Not yet implemented")
+            }
+        }
+    )
+}
+
+internal class TestComplicationProviderDefaultsWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder
+) : WatchFaceService() {
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): ComplicationSlotsManager {
+        return ComplicationSlotsManager(
+            listOf(
+                ComplicationSlot.createRoundRectComplicationSlotBuilder(
+                    123,
+                    { _, _ ->
+                        object : CanvasComplication {
+                            override fun render(
+                                canvas: Canvas,
+                                bounds: Rect,
+                                zonedDateTime: ZonedDateTime,
+                                renderParameters: RenderParameters,
+                                slotId: Int
+                            ) {
+                            }
+
+                            override fun drawHighlight(
+                                canvas: Canvas,
+                                bounds: Rect,
+                                boundsType: Int,
+                                zonedDateTime: ZonedDateTime,
+                                color: Int
+                            ) {
+                            }
+
+                            override fun getData() = NoDataComplicationData()
+
+                            override fun loadData(
+                                complicationData: ComplicationData,
+                                loadDrawablesAsynchronous: Boolean
+                            ) {
+                            }
+                        }
+                    },
+                    listOf(
+                        ComplicationType.PHOTO_IMAGE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT
+                    ),
+                    DefaultComplicationDataSourcePolicy(
+                        ComponentName("com.package1", "com.app1"),
+                        ComplicationType.PHOTO_IMAGE,
+                        ComponentName("com.package2", "com.app2"),
+                        ComplicationType.LONG_TEXT,
+                        SystemDataSources.DATA_SOURCE_STEP_COUNT,
+                        ComplicationType.SHORT_TEXT
+                    ),
+                    ComplicationSlotBounds(
+                        RectF(0.1f, 0.2f, 0.3f, 0.4f)
+                    )
+                )
+                    .build()
+            ),
+            currentUserStyleRepository
+        )
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.DIGITAL,
+        @Suppress("deprecation")
+        object : Renderer.CanvasRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            CanvasType.HARDWARE,
+            16
+        ) {
+            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+
+            override fun renderHighlightLayer(
+                canvas: Canvas,
+                bounds: Rect,
+                zonedDateTime: ZonedDateTime
+            ) {
+            }
+        }
+    )
+}
+
+internal class TestEdgeComplicationWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder
+) : WatchFaceService() {
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    @OptIn(ComplicationExperimental::class)
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): ComplicationSlotsManager {
+        return ComplicationSlotsManager(
+            listOf(
+                ComplicationSlot.createEdgeComplicationSlotBuilder(
+                    123,
+                    { _, _ ->
+                        object : CanvasComplication {
+                            override fun render(
+                                canvas: Canvas,
+                                bounds: Rect,
+                                zonedDateTime: ZonedDateTime,
+                                renderParameters: RenderParameters,
+                                slotId: Int
+                            ) {
+                            }
+
+                            override fun drawHighlight(
+                                canvas: Canvas,
+                                bounds: Rect,
+                                boundsType: Int,
+                                zonedDateTime: ZonedDateTime,
+                                color: Int
+                            ) {
+                            }
+
+                            override fun getData() = NoDataComplicationData()
+
+                            override fun loadData(
+                                complicationData: ComplicationData,
+                                loadDrawablesAsynchronous: Boolean
+                            ) {
+                            }
+                        }
+                    },
+                    listOf(
+                        ComplicationType.PHOTO_IMAGE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT
+                    ),
+                    DefaultComplicationDataSourcePolicy(
+                        ComponentName("com.package1", "com.app1"),
+                        ComplicationType.PHOTO_IMAGE,
+                        ComponentName("com.package2", "com.app2"),
+                        ComplicationType.LONG_TEXT,
+                        SystemDataSources.DATA_SOURCE_STEP_COUNT,
+                        ComplicationType.SHORT_TEXT
+                    ),
+                    ComplicationSlotBounds(
+                        RectF(0f, 0f, 1f, 1f)
+                    ),
+                    BoundingArc(45f, 90f, 0.1f)
+                )
+                    .build()
+            ),
+            currentUserStyleRepository
+        )
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.DIGITAL,
+        @Suppress("deprecation")
+        object : Renderer.CanvasRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            CanvasType.HARDWARE,
+            16
+        ) {
+            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+
+            override fun renderHighlightLayer(
+                canvas: Canvas,
+                bounds: Rect,
+                zonedDateTime: ZonedDateTime
+            ) {
+            }
+        }
+    )
+}
+
+internal class TestWatchFaceServiceWithPreviewImageUpdateRequest(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder,
+) : WatchFaceService() {
+    val rendererInitializedLatch = CountDownLatch(1)
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    @Suppress("deprecation")
+    private lateinit var renderer: Renderer.CanvasRenderer
+
+    fun triggerPreviewImageUpdateRequest() {
+        renderer.sendPreviewImageNeedsUpdateRequest()
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): WatchFace {
+        @Suppress("deprecation")
+        renderer = object : Renderer.CanvasRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            CanvasType.HARDWARE,
+            16
+        ) {
+            override suspend fun init() {
+                rendererInitializedLatch.countDown()
+            }
+
+            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+
+            override fun renderHighlightLayer(
+                canvas: Canvas,
+                bounds: Rect,
+                zonedDateTime: ZonedDateTime
+            ) {
+            }
+        }
+        return WatchFace(WatchFaceType.DIGITAL, renderer)
+    }
+}
+
+internal class TestComplicationStyleUpdateWatchFaceService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder
+) : WatchFaceService() {
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    @Suppress("deprecation")
+    private val complicationsStyleSetting =
+        UserStyleSetting.ComplicationSlotsUserStyleSetting(
+            UserStyleSetting.Id(COMPLICATIONS_STYLE_SETTING),
+            resources,
+            R.string.watchface_complications_setting,
+            R.string.watchface_complications_setting_description,
+            icon = null,
+            complicationConfig = listOf(
+                UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+                    UserStyleSetting.Option.Id(NO_COMPLICATIONS),
+                    resources,
+                    R.string.watchface_complications_setting_none,
+                    null,
+                    listOf(
+                        UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+                            123,
+                            enabled = false
+                        )
+                    )
+                ),
+                UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+                    UserStyleSetting.Option.Id(LEFT_COMPLICATION),
+                    resources,
+                    R.string.watchface_complications_setting_left,
+                    null,
+                    listOf(
+                        UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+                            123,
+                            enabled = true,
+                            nameResourceId = R.string.left_complication_screen_name,
+                            screenReaderNameResourceId =
+                            R.string.left_complication_screen_reader_name
+                        )
+                    )
+                )
+            ),
+            listOf(WatchFaceLayer.COMPLICATIONS)
+        )
+
+    override fun createUserStyleSchema(): UserStyleSchema =
+        UserStyleSchema(listOf(complicationsStyleSetting))
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): ComplicationSlotsManager {
+        return ComplicationSlotsManager(
+            listOf(
+                ComplicationSlot.createRoundRectComplicationSlotBuilder(
+                    123,
+                    { _, _ ->
+                        object : CanvasComplication {
+                            override fun render(
+                                canvas: Canvas,
+                                bounds: Rect,
+                                zonedDateTime: ZonedDateTime,
+                                renderParameters: RenderParameters,
+                                slotId: Int
+                            ) {
+                            }
+
+                            override fun drawHighlight(
+                                canvas: Canvas,
+                                bounds: Rect,
+                                boundsType: Int,
+                                zonedDateTime: ZonedDateTime,
+                                color: Int
+                            ) {
+                            }
+
+                            override fun getData() = NoDataComplicationData()
+
+                            override fun loadData(
+                                complicationData: ComplicationData,
+                                loadDrawablesAsynchronous: Boolean
+                            ) {
+                            }
+                        }
+                    },
+                    listOf(
+                        ComplicationType.PHOTO_IMAGE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT
+                    ),
+                    DefaultComplicationDataSourcePolicy(
+                        ComponentName("com.package1", "com.app1"),
+                        ComplicationType.PHOTO_IMAGE,
+                        ComponentName("com.package2", "com.app2"),
+                        ComplicationType.LONG_TEXT,
+                        SystemDataSources.DATA_SOURCE_STEP_COUNT,
+                        ComplicationType.SHORT_TEXT
+                    ),
+                    ComplicationSlotBounds(
+                        RectF(0.1f, 0.2f, 0.3f, 0.4f)
+                    )
+                ).build()
+            ),
+            currentUserStyleRepository
+        )
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) = WatchFace(
+        WatchFaceType.ANALOG,
+        @Suppress("deprecation")
+        object : Renderer.CanvasRenderer(
+            surfaceHolder,
+            currentUserStyleRepository,
+            watchState,
+            CanvasType.HARDWARE,
+            16
+        ) {
+            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+
+            override fun renderHighlightLayer(
+                canvas: Canvas,
+                bounds: Rect,
+                zonedDateTime: ZonedDateTime
+            ) {
+            }
+        }
+    )
+}
+
+internal object TestServicesHelpers {
+    fun createTestComplications(context: Context) = mapOf(
+        ExampleCanvasAnalogWatchFaceService.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID to
+            ShortTextComplicationData.Builder(
+                PlainComplicationText.Builder("ID").build(),
+                ComplicationText.EMPTY
+            ).setTitle(PlainComplicationText.Builder("Left").build())
+                .setTapAction(
+                    PendingIntent.getActivity(context, 0, Intent("left"),
+                        PendingIntent.FLAG_IMMUTABLE
+                    )
+                )
+                .build(),
+        ExampleCanvasAnalogWatchFaceService.EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID to
+            ShortTextComplicationData.Builder(
+                PlainComplicationText.Builder("ID").build(),
+                ComplicationText.EMPTY
+            ).setTitle(PlainComplicationText.Builder("Right").build())
+                .setTapAction(
+                    PendingIntent.getActivity(context, 0, Intent("right"),
+                        PendingIntent.FLAG_IMMUTABLE
+                    )
+                )
+                .build()
+    )
+
+    inline fun <reified T>componentOf(): ComponentName {
+        return ComponentName(
+            T::class.java.`package`?.name!!,
+            T::class.java.name
+        )
+    }
+}
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 99b7e4e..a10788a 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -18,7 +18,6 @@
 
 import android.annotation.SuppressLint
 import android.app.PendingIntent
-import android.app.PendingIntent.FLAG_IMMUTABLE
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
@@ -26,11 +25,9 @@
 import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Rect
-import android.graphics.RectF
 import android.graphics.SurfaceTexture
 import android.os.Build
 import android.os.Handler
-import android.os.IBinder
 import android.os.Looper
 import android.view.Surface
 import android.view.SurfaceHolder
@@ -41,21 +38,15 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import androidx.test.screenshot.assertAgainstGolden
 import androidx.wear.watchface.BoundingArc
-import androidx.wear.watchface.CanvasComplication
-import androidx.wear.watchface.CanvasType
 import androidx.wear.watchface.ComplicationSlot
 import androidx.wear.watchface.ComplicationSlotBoundsType
-import androidx.wear.watchface.ComplicationSlotsManager
 import androidx.wear.watchface.ContentDescriptionLabel
 import androidx.wear.watchface.DrawMode
 import androidx.wear.watchface.RenderParameters
-import androidx.wear.watchface.Renderer
 import androidx.wear.watchface.WatchFace
 import androidx.wear.watchface.WatchFaceColors
 import androidx.wear.watchface.WatchFaceExperimental
 import androidx.wear.watchface.WatchFaceService
-import androidx.wear.watchface.WatchFaceType
-import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.client.DeviceConfig
 import androidx.wear.watchface.client.DisconnectReason
 import androidx.wear.watchface.client.DisconnectReasons
@@ -64,7 +55,8 @@
 import androidx.wear.watchface.client.WatchFaceClientExperimental
 import androidx.wear.watchface.client.WatchFaceControlClient
 import androidx.wear.watchface.client.WatchUiState
-import androidx.wear.watchface.complications.ComplicationSlotBounds
+import androidx.wear.watchface.client.test.TestServicesHelpers.componentOf
+import androidx.wear.watchface.client.test.TestServicesHelpers.createTestComplications
 import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
 import androidx.wear.watchface.complications.SystemDataSources
 import androidx.wear.watchface.complications.data.ComplicationData
@@ -72,11 +64,8 @@
 import androidx.wear.watchface.complications.data.ComplicationText
 import androidx.wear.watchface.complications.data.ComplicationType
 import androidx.wear.watchface.complications.data.LongTextComplicationData
-import androidx.wear.watchface.complications.data.NoDataComplicationData
 import androidx.wear.watchface.complications.data.PlainComplicationText
 import androidx.wear.watchface.complications.data.RangedValueComplicationData
-import androidx.wear.watchface.complications.data.ShortTextComplicationData
-import androidx.wear.watchface.control.IInteractiveWatchFace
 import androidx.wear.watchface.control.WatchFaceControlService
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.BLUE_STYLE
@@ -88,26 +77,22 @@
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.GREEN_STYLE
-import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.LEFT_COMPLICATION
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.NO_COMPLICATIONS
 import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Companion.WATCH_HAND_LENGTH_STYLE_SETTING
 import androidx.wear.watchface.samples.ExampleOpenGLBackgroundInitWatchFaceService
 import androidx.wear.watchface.samples.R
-import androidx.wear.watchface.style.CurrentUserStyleRepository
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.UserStyleData
-import androidx.wear.watchface.style.UserStyleSchema
-import androidx.wear.watchface.style.UserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.BooleanUserStyleSetting.BooleanOption
 import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting.DoubleRangeOption
 import androidx.wear.watchface.style.WatchFaceLayer
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
-import java.time.ZoneId
-import java.time.ZonedDateTime
 import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.TimeoutException
+import java.util.function.Consumer
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Deferred
@@ -132,12 +117,10 @@
 private const val DESTROY_TIMEOUT_MILLIS = 500L
 private const val UPDATE_TIMEOUT_MILLIS = 500L
 
-@RunWith(AndroidJUnit4::class)
-@MediumTest
 @RequiresApi(Build.VERSION_CODES.O_MR1)
-class WatchFaceControlClientTest {
-    private val context = ApplicationProvider.getApplicationContext<Context>()
-    private val service = runBlocking {
+abstract class WatchFaceControlClientTestBase {
+    protected val context: Context = ApplicationProvider.getApplicationContext()
+    protected val service = runBlocking {
         WatchFaceControlClient.createWatchFaceControlClientImpl(
             context,
             Intent(context, WatchFaceControlTestService::class.java).apply {
@@ -147,31 +130,35 @@
     }
 
     @Mock
-    private lateinit var mockBinder: IBinder
+    protected lateinit var surfaceHolder: SurfaceHolder
 
     @Mock
-    private lateinit var iInteractiveWatchFace: IInteractiveWatchFace
-
-    @Mock
-    private lateinit var surfaceHolder: SurfaceHolder
-
-    @Mock
-    private lateinit var surfaceHolder2: SurfaceHolder
+    protected lateinit var surfaceHolder2: SurfaceHolder
 
     @Mock
     private lateinit var surface: Surface
-    private lateinit var engine: WatchFaceService.EngineWrapper
-    private val handler = Handler(Looper.getMainLooper())
-    private val handlerCoroutineScope =
+
+    protected val handler = Handler(Looper.getMainLooper())
+    protected val handlerCoroutineScope =
         CoroutineScope(Handler(handler.looper).asCoroutineDispatcher())
-    private lateinit var wallpaperService: WatchFaceService
+
+    protected lateinit var engine: WatchFaceService.EngineWrapper
+
+    protected val deviceConfig = DeviceConfig(
+        hasLowBitAmbient = false,
+        hasBurnInProtection = false,
+        analogPreviewReferenceTimeMillis = 0,
+        digitalPreviewReferenceTimeMillis = 0
+    )
+
+    protected val systemState = WatchUiState(false, 0)
+
+    protected val complications = createTestComplications(context)
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         WatchFaceControlTestService.apiVersionOverride = null
-        wallpaperService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
-
         Mockito.`when`(surfaceHolder.surfaceFrame)
             .thenReturn(Rect(0, 0, 400, 400))
         Mockito.`when`(surfaceHolder.surface).thenReturn(surface)
@@ -193,59 +180,48 @@
         service.close()
     }
 
-    @get:Rule
-    val screenshotRule: AndroidXScreenshotTestRule =
-        AndroidXScreenshotTestRule("wear/wear-watchface-client")
-
-    private val exampleCanvasAnalogWatchFaceComponentName = ComponentName(
-        "androidx.wear.watchface.samples.test",
-        "androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService"
-    )
-
-    private val exampleOpenGLWatchFaceComponentName = ComponentName(
-        "androidx.wear.watchface.samples.test",
-        "androidx.wear.watchface.samples.ExampleOpenGLBackgroundInitWatchFaceService"
-    )
-
-    private val deviceConfig = DeviceConfig(
-        false,
-        false,
-        0,
-        0
-    )
-
-    private val systemState = WatchUiState(false, 0)
-
-    private val complications = mapOf(
-        EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID to
-            ShortTextComplicationData.Builder(
-                PlainComplicationText.Builder("ID").build(),
-                ComplicationText.EMPTY
-            ).setTitle(PlainComplicationText.Builder("Left").build())
-                .setTapAction(
-                    PendingIntent.getActivity(context, 0, Intent("left"), FLAG_IMMUTABLE)
+    protected fun getOrCreateTestSubject(
+        watchFaceService: WatchFaceService =
+            TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder),
+        instanceId: String = "testId",
+        userStyle: UserStyleData? = null,
+        complications: Map<Int, ComplicationData>? = this.complications,
+        previewExecutor: Executor? = null,
+        previewListener: Consumer<String>? = null
+    ): InteractiveWatchFaceClient {
+        val deferredInteractiveInstance = handlerCoroutineScope.async {
+            if (previewExecutor != null && previewListener != null) {
+                service.getOrCreateInteractiveWatchFaceClient(
+                    instanceId,
+                    deviceConfig,
+                    systemState,
+                    userStyle,
+                    complications,
+                    previewExecutor,
+                    previewListener
                 )
-                .build(),
-        EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID to
-            ShortTextComplicationData.Builder(
-                PlainComplicationText.Builder("ID").build(),
-                ComplicationText.EMPTY
-            ).setTitle(PlainComplicationText.Builder("Right").build())
-                .setTapAction(
-                    PendingIntent.getActivity(context, 0, Intent("right"), FLAG_IMMUTABLE)
+            } else {
+                @Suppress("deprecation")
+                service.getOrCreateInteractiveWatchFaceClient(
+                    instanceId,
+                    deviceConfig,
+                    systemState,
+                    userStyle,
+                    complications
                 )
-                .build()
-    )
-
-    private fun createEngine() {
-        // onCreateEngine must run after getOrCreateInteractiveWatchFaceClient. To ensure the
-        // ordering relationship both calls should run on the same handler.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
+            }
         }
+
+        // Create the engine which triggers construction of the interactive instance.
+        handler.post {
+            engine = watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
+        }
+
+        // Wait for the instance to be created.
+        return awaitWithTimeout(deferredInteractiveInstance)
     }
 
-    private fun <X> awaitWithTimeout(
+    protected fun <X> awaitWithTimeout(
         thing: Deferred<X>,
         timeoutMillis: Long = CONNECT_TIMEOUT_MILLIS
     ): X {
@@ -260,173 +236,14 @@
         }
         return value!!
     }
+}
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+class WatchFaceControlClientTest : WatchFaceControlClientTestBase() {
 
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun headlessScreenshot() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            DeviceConfig(
-                false,
-                false,
-                0,
-                0
-            ),
-            400,
-            400
-        )!!
-        val bitmap = headlessInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        bitmap.assertAgainstGolden(screenshotRule, "headlessScreenshot")
-
-        headlessInstance.close()
-    }
-
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun yellowComplicationHighlights() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            DeviceConfig(
-                false,
-                false,
-                0,
-                0
-            ),
-            400,
-            400
-        )!!
-        val bitmap = headlessInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                RenderParameters.HighlightLayer(
-                    RenderParameters.HighlightedElement.AllComplicationSlots,
-                    Color.YELLOW,
-                    Color.argb(128, 0, 0, 0) // Darken everything else.
-                )
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        bitmap.assertAgainstGolden(screenshotRule, "yellowComplicationHighlights")
-
-        headlessInstance.close()
-    }
-
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun highlightOnlyLayer() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            DeviceConfig(
-                false,
-                false,
-                0,
-                0
-            ),
-            400,
-            400
-        )!!
-        val bitmap = headlessInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                emptySet(),
-                RenderParameters.HighlightLayer(
-                    RenderParameters.HighlightedElement.AllComplicationSlots,
-                    Color.YELLOW,
-                    Color.argb(128, 0, 0, 0) // Darken everything else.
-                )
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        bitmap.assertAgainstGolden(screenshotRule, "highlightOnlyLayer")
-
-        headlessInstance.close()
-    }
-
-    @Suppress("DEPRECATION", "NewApi") // defaultDataSourceType
-    @Test
-    fun headlessComplicationDetails() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            deviceConfig,
-            400,
-            400
-        )!!
-
-        assertThat(headlessInstance.complicationSlotsState.size).isEqualTo(2)
-
-        val leftComplicationDetails = headlessInstance.complicationSlotsState[
-            EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
-        ]!!
-        assertThat(leftComplicationDetails.bounds).isEqualTo(Rect(80, 160, 160, 240))
-        assertThat(leftComplicationDetails.boundsType)
-            .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
-        assertThat(
-            leftComplicationDetails.defaultDataSourcePolicy.systemDataSourceFallback
-        ).isEqualTo(
-            SystemDataSources.DATA_SOURCE_DAY_OF_WEEK
-        )
-        assertThat(leftComplicationDetails.defaultDataSourceType).isEqualTo(
-            ComplicationType.SHORT_TEXT
-        )
-        assertThat(leftComplicationDetails.supportedTypes).containsExactly(
-            ComplicationType.RANGED_VALUE,
-            ComplicationType.GOAL_PROGRESS,
-            ComplicationType.WEIGHTED_ELEMENTS,
-            ComplicationType.LONG_TEXT,
-            ComplicationType.SHORT_TEXT,
-            ComplicationType.MONOCHROMATIC_IMAGE,
-            ComplicationType.SMALL_IMAGE
-        )
-        assertTrue(leftComplicationDetails.isEnabled)
-
-        val rightComplicationDetails = headlessInstance.complicationSlotsState[
-            EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
-        ]!!
-        assertThat(rightComplicationDetails.bounds).isEqualTo(Rect(240, 160, 320, 240))
-        assertThat(rightComplicationDetails.boundsType)
-            .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
-        assertThat(
-            rightComplicationDetails.defaultDataSourcePolicy.systemDataSourceFallback
-        ).isEqualTo(
-            SystemDataSources.DATA_SOURCE_STEP_COUNT
-        )
-        assertThat(rightComplicationDetails.defaultDataSourceType).isEqualTo(
-            ComplicationType.SHORT_TEXT
-        )
-        assertThat(rightComplicationDetails.supportedTypes).containsExactly(
-            ComplicationType.RANGED_VALUE,
-            ComplicationType.GOAL_PROGRESS,
-            ComplicationType.WEIGHTED_ELEMENTS,
-            ComplicationType.LONG_TEXT,
-            ComplicationType.SHORT_TEXT,
-            ComplicationType.MONOCHROMATIC_IMAGE,
-            ComplicationType.SMALL_IMAGE
-        )
-        assertTrue(rightComplicationDetails.isEnabled)
-
-        headlessInstance.close()
-    }
+    private val exampleCanvasAnalogWatchFaceComponentName =
+        componentOf<ExampleCanvasAnalogWatchFaceService>()
 
     @Test
     fun complicationProviderDefaults() {
@@ -434,23 +251,9 @@
             context,
             surfaceHolder
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-        // Create the engine which triggers construction of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(
+            wallpaperService
+        )
 
         try {
             assertThat(interactiveInstance.complicationSlotsState.keys).containsExactly(123)
@@ -480,23 +283,9 @@
             context,
             surfaceHolder
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-        // Create the engine which triggers construction of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(
+            wallpaperService
+        )
 
         try {
             assertThat(interactiveInstance.complicationSlotsState.keys).containsExactly(123)
@@ -516,23 +305,8 @@
             context,
             surfaceHolder
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-        // Create the engine which triggers construction of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
 
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         // User style settings to be updated
         val userStyleSettings = interactiveInstance.userStyleSchema.userStyleSettings
@@ -561,176 +335,10 @@
         }
     }
 
-    @Test
-    @Suppress("Deprecation") // userStyleSettings
-    fun headlessUserStyleSchema() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            deviceConfig,
-            400,
-            400
-        )!!
-
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings.size).isEqualTo(5)
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings[0].id.value).isEqualTo(
-            "color_style_setting"
-        )
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings[1].id.value).isEqualTo(
-            "draw_hour_pips_style_setting"
-        )
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings[2].id.value).isEqualTo(
-            "watch_hand_length_style_setting"
-        )
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings[3].id.value).isEqualTo(
-            "complications_style_setting"
-        )
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings[4].id.value).isEqualTo(
-            "hours_draw_freq_style_setting"
-        )
-
-        headlessInstance.close()
-    }
-
-    @Test
-    fun headlessUserStyleFlavors() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            deviceConfig,
-            400,
-            400
-        )!!
-
-        assertThat(headlessInstance.getUserStyleFlavors().flavors.size).isEqualTo(1)
-        val flavorA = headlessInstance.getUserStyleFlavors().flavors[0]
-        assertThat(flavorA.id).isEqualTo("exampleFlavor")
-        assertThat(flavorA.style.userStyleMap.containsKey("color_style_setting"))
-        assertThat(flavorA.style.userStyleMap.containsKey("watch_hand_length_style_setting"))
-        assertThat(flavorA.complications.containsKey(EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID))
-        assertThat(
-            flavorA.complications.containsKey(
-                EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
-            )
-        )
-
-        headlessInstance.close()
-    }
-
-    @Test
-    @Suppress("Deprecation") // userStyleSettings
-    fun headlessToBundleAndCreateFromBundle() {
-        val headlessInstance = HeadlessWatchFaceClient.createFromBundle(
-            service.createHeadlessWatchFaceClient(
-                "id",
-                exampleCanvasAnalogWatchFaceComponentName,
-                deviceConfig,
-                400,
-                400
-            )!!.toBundle()
-        )
-
-        assertThat(headlessInstance.userStyleSchema.userStyleSettings.size).isEqualTo(5)
-    }
-
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun getOrCreateInteractiveWatchFaceClient() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
-
-        val bitmap = interactiveInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        try {
-            bitmap.assertAgainstGolden(screenshotRule, "interactiveScreenshot")
-        } finally {
-            interactiveInstance.close()
-        }
-    }
-
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun getOrCreateInteractiveWatchFaceClient_initialStyle() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                // An incomplete map which is OK.
-                UserStyleData(
-                    mapOf(
-                        "color_style_setting" to "green_style".encodeToByteArray(),
-                        "draw_hour_pips_style_setting" to BooleanOption.FALSE.id.value,
-                        "watch_hand_length_style_setting" to DoubleRangeOption(0.8).id.value
-                    )
-                ),
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
-
-        val bitmap = interactiveInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        try {
-            bitmap.assertAgainstGolden(screenshotRule, "initialStyle")
-        } finally {
-            interactiveInstance.close()
-        }
-    }
-
     @Suppress("DEPRECATION", "newApi") // defaultDataSourceType & ComplicationType
     @Test
     fun interactiveWatchFaceClient_ComplicationDetails() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject()
 
         assertThat(interactiveInstance.complicationSlotsState.size).isEqualTo(2)
 
@@ -762,9 +370,9 @@
             ComplicationType.SHORT_TEXT
         )
         assertThat(leftComplicationDetails.nameResourceId)
-            .isEqualTo(androidx.wear.watchface.samples.R.string.left_complication_screen_name)
+            .isEqualTo(R.string.left_complication_screen_name)
         assertThat(leftComplicationDetails.screenReaderNameResourceId).isEqualTo(
-            androidx.wear.watchface.samples.R.string.left_complication_screen_reader_name
+            R.string.left_complication_screen_reader_name
         )
 
         val rightComplicationDetails = interactiveInstance.complicationSlotsState[
@@ -793,31 +401,17 @@
             ComplicationType.SHORT_TEXT
         )
         assertThat(rightComplicationDetails.nameResourceId)
-            .isEqualTo(androidx.wear.watchface.samples.R.string.right_complication_screen_name)
+            .isEqualTo(R.string.right_complication_screen_name)
         assertThat(rightComplicationDetails.screenReaderNameResourceId).isEqualTo(
-            androidx.wear.watchface.samples.R.string.right_complication_screen_reader_name
+            R.string.right_complication_screen_reader_name
         )
 
         interactiveInstance.close()
     }
 
     @Test
-    public fun updateComplicationData() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+    fun updateComplicationData() {
+        val interactiveInstance = getOrCreateTestSubject()
 
         // Under the hood updateComplicationData is a oneway aidl method so we need to perform some
         // additional synchronization to ensure it's side effects have been applied before
@@ -877,21 +471,9 @@
 
     @Test
     fun getOrCreateInteractiveWatchFaceClient_existingOpenInstance() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
+        val watchFaceService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
 
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        awaitWithTimeout(deferredInteractiveInstance)
+        getOrCreateTestSubject(watchFaceService)
 
         val deferredInteractiveInstance2 = handlerCoroutineScope.async {
             @Suppress("deprecation")
@@ -904,91 +486,22 @@
             )
         }
 
-        assertThat(awaitWithTimeout(deferredInteractiveInstance2).instanceId).isEqualTo("testId")
-    }
-
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun getOrCreateInteractiveWatchFaceClient_existingOpenInstance_styleChange() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        awaitWithTimeout(deferredInteractiveInstance)
-
-        val deferredInteractiveInstance2 = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                UserStyleData(
-                    mapOf(
-                        "color_style_setting" to "blue_style".encodeToByteArray(),
-                        "draw_hour_pips_style_setting" to BooleanOption.FALSE.id.value,
-                        "watch_hand_length_style_setting" to DoubleRangeOption(0.25).id.value
-                    )
-                ),
-                complications
-            )
-        }
-
-        val interactiveInstance2 = awaitWithTimeout(deferredInteractiveInstance2)
-        assertThat(interactiveInstance2.instanceId).isEqualTo("testId")
-
-        val bitmap = interactiveInstance2.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        try {
-            // Note the hour hand pips and both complicationSlots should be visible in this image.
-            bitmap.assertAgainstGolden(screenshotRule, "existingOpenInstance_styleChange")
-        } finally {
-            interactiveInstance2.close()
-        }
+        assertThat(awaitWithTimeout(deferredInteractiveInstance2).instanceId)
+            .isEqualTo("testId")
     }
 
     @Test
     fun getOrCreateInteractiveWatchFaceClient_existingClosedInstance() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
+        val wallpaperService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
 
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         // Closing this interface means the subsequent
         // getOrCreateInteractiveWatchFaceClient won't immediately return
         // a resolved future.
         interactiveInstance.close()
 
+        // Connect again to the same wallpaperService instance
         val deferredExistingInstance = handlerCoroutineScope.async {
             @Suppress("deprecation")
             service.getOrCreateInteractiveWatchFaceClient(
@@ -1012,25 +525,13 @@
 
     @Test
     fun getInteractiveWatchFaceInstance() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
+        val testId = "testId"
+        // Create and wait for an interactive instance without capturing a reference to it
+        getOrCreateTestSubject(instanceId = testId)
 
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        // Wait for the instance to be created.
-        awaitWithTimeout(deferredInteractiveInstance)
-
+        // Get the instance created above
         val sysUiInterface =
-            service.getInteractiveWatchFaceClientInstance("testId")!!
+            service.getInteractiveWatchFaceClientInstance(testId)!!
 
         val contentDescriptionLabels = sysUiInterface.contentDescriptionLabels
         assertThat(contentDescriptionLabels.size).isEqualTo(3)
@@ -1061,22 +562,9 @@
 
     @Test
     fun additionalContentDescriptionLabels() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
+        val wallpaperService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
 
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         // We need to wait for watch face init to have completed before lateinit
         // wallpaperService.watchFace will be assigned. To do this we issue an arbitrary API
@@ -1092,7 +580,7 @@
             context, 0, Intent("Two"),
             PendingIntent.FLAG_IMMUTABLE
         )
-        (wallpaperService as TestExampleCanvasAnalogWatchFaceService)
+        (wallpaperService)
             .watchFace.renderer.additionalContentDescriptionLabels = listOf(
             Pair(
                 0,
@@ -1154,115 +642,16 @@
 
     @Test
     fun contentDescriptionLabels_after_close() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject()
 
         assertThat(interactiveInstance.contentDescriptionLabels).isNotEmpty()
         interactiveInstance.close()
         assertThat(interactiveInstance.contentDescriptionLabels).isEmpty()
     }
 
-    @SuppressLint("NewApi") // renderWatchFaceToBitmap
-    @Test
-    fun updateInstance() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                UserStyleData(
-                    mapOf(
-                        COLOR_STYLE_SETTING to GREEN_STYLE.encodeToByteArray(),
-                        WATCH_HAND_LENGTH_STYLE_SETTING to DoubleRangeOption(0.25).id.value,
-                        DRAW_HOUR_PIPS_STYLE_SETTING to BooleanOption.FALSE.id.value,
-                        COMPLICATIONS_STYLE_SETTING to NO_COMPLICATIONS.encodeToByteArray()
-                    )
-                ),
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
-
-        assertThat(interactiveInstance.instanceId).isEqualTo("testId")
-
-        // Note this map doesn't include all the categories, which is fine the others will be set
-        // to their defaults.
-        interactiveInstance.updateWatchFaceInstance(
-            "testId2",
-            UserStyleData(
-                mapOf(
-                    COLOR_STYLE_SETTING to BLUE_STYLE.encodeToByteArray(),
-                    WATCH_HAND_LENGTH_STYLE_SETTING to DoubleRangeOption(0.9).id.value,
-                )
-            )
-        )
-
-        assertThat(interactiveInstance.instanceId).isEqualTo("testId2")
-
-        // It should be possible to create an instance with the updated id.
-        val instance =
-            service.getInteractiveWatchFaceClientInstance("testId2")
-        assertThat(instance).isNotNull()
-        instance?.close()
-
-        // The previous instance should still be usable despite the new instance being closed.
-        interactiveInstance.updateComplicationData(complications)
-        val bitmap = interactiveInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            complications
-        )
-
-        try {
-            // Note the hour hand pips and both complicationSlots should be visible in this image.
-            bitmap.assertAgainstGolden(screenshotRule, "setUserStyle")
-        } finally {
-            interactiveInstance.close()
-        }
-    }
-
     @Test
     fun getComplicationIdAt() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject()
 
         assertNull(interactiveInstance.getComplicationIdAt(0, 0))
         assertThat(interactiveInstance.getComplicationIdAt(85, 165)).isEqualTo(
@@ -1491,23 +880,8 @@
             onUiThreadGlSurfaceCreatedCompletableDeferred,
             onBackgroundThreadGlContextCreatedCompletableDeferred
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-        // Create the engine which triggers creation of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
 
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         try {
             val wfReady = CompletableDeferred<Unit>()
@@ -1529,21 +903,8 @@
 
     @Test
     fun isConnectionAlive_false_after_close() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
+        val interactiveInstance = getOrCreateTestSubject()
 
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
         assertThat(interactiveInstance.isConnectionAlive()).isTrue()
 
         interactiveInstance.close()
@@ -1561,74 +922,6 @@
         assertTrue(service.hasComplicationDataCache())
     }
 
-    @Ignore // b/225230182
-    @Test
-    fun interactiveAndHeadlessOpenGlWatchFaceInstances() {
-        val surfaceTexture = SurfaceTexture(false)
-        surfaceTexture.setDefaultBufferSize(400, 400)
-        Mockito.`when`(surfaceHolder2.surface).thenReturn(Surface(surfaceTexture))
-        Mockito.`when`(surfaceHolder2.surfaceFrame)
-            .thenReturn(Rect(0, 0, 400, 400))
-
-        wallpaperService = TestExampleOpenGLBackgroundInitWatchFaceService(context, surfaceHolder2)
-
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                emptyMap()
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
-        val headlessInstance = HeadlessWatchFaceClient.createFromBundle(
-            service.createHeadlessWatchFaceClient(
-                "id",
-                exampleOpenGLWatchFaceComponentName,
-                deviceConfig,
-                200,
-                200
-            )!!.toBundle()
-        )
-
-        // Take screenshots from both instances to confirm rendering works as expected despite the
-        // watch face using shared SharedAssets.
-        val interactiveBitmap = interactiveInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            null
-        )
-
-        interactiveBitmap.assertAgainstGolden(screenshotRule, "opengl_interactive")
-
-        val headlessBitmap = headlessInstance.renderWatchFaceToBitmap(
-            RenderParameters(
-                DrawMode.INTERACTIVE,
-                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
-                null
-            ),
-            Instant.ofEpochMilli(1234567),
-            null,
-            null
-        )
-
-        headlessBitmap.assertAgainstGolden(screenshotRule, "opengl_headless")
-
-        headlessInstance.close()
-        interactiveInstance.close()
-    }
-
     @Test
     fun watchfaceOverlayStyle() {
         val wallpaperService = TestWatchfaceOverlayStyleWatchFaceService(
@@ -1636,24 +929,8 @@
             surfaceHolder,
             WatchFace.OverlayStyle(Color.valueOf(Color.RED), Color.valueOf(Color.BLACK))
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
 
-        // Create the engine which triggers creation of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         assertThat(interactiveInstance.overlayStyle.backgroundColor)
             .isEqualTo(Color.valueOf(Color.RED))
@@ -1670,24 +947,8 @@
             surfaceHolder,
             WatchFace.OverlayStyle(Color.valueOf(Color.RED), Color.valueOf(Color.BLACK))
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
 
-        // Create the engine which triggers creation of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         interactiveInstance.close()
 
@@ -1696,57 +957,14 @@
     }
 
     @Test
-    fun computeUserStyleSchemaDigestHash() {
-        val headlessInstance1 = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleCanvasAnalogWatchFaceComponentName,
-            DeviceConfig(
-                false,
-                false,
-                0,
-                0
-            ),
-            400,
-            400
-        )!!
-
-        val headlessInstance2 = service.createHeadlessWatchFaceClient(
-            "id",
-            exampleOpenGLWatchFaceComponentName,
-            deviceConfig,
-            400,
-            400
-        )!!
-
-        assertThat(headlessInstance1.getUserStyleSchemaDigestHash()).isNotEqualTo(
-            headlessInstance2.getUserStyleSchemaDigestHash()
-        )
-    }
-
-    @Test
     @OptIn(ComplicationExperimental::class)
     fun edgeComplication_boundingArc() {
         val wallpaperService = TestEdgeComplicationWatchFaceService(
             context,
             surfaceHolder
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-        // Create the engine which triggers construction of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
 
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         try {
             assertThat(interactiveInstance.complicationSlotsState.keys).containsExactly(123)
@@ -1760,49 +978,10 @@
         }
     }
 
-    @Test
-    fun headlessLifeCycle() {
-        val headlessInstance = service.createHeadlessWatchFaceClient(
-            "id",
-            ComponentName(
-                "androidx.wear.watchface.client.test",
-                "androidx.wear.watchface.client.test.TestLifeCycleWatchFaceService"
-            ),
-            deviceConfig,
-            400,
-            400
-        )!!
-
-        // Blocks until the headless instance has been fully constructed.
-        headlessInstance.previewReferenceInstant
-        headlessInstance.close()
-
-        assertThat(TestLifeCycleWatchFaceService.lifeCycleEvents).containsExactly(
-            "WatchFaceService.onCreate",
-            "Renderer.constructed",
-            "Renderer.onDestroy",
-            "WatchFaceService.onDestroy"
-        )
-    }
-
     @OptIn(WatchFaceClientExperimental::class, WatchFaceExperimental::class)
     @Test
     fun watchFaceColors() {
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-
-        // Create the engine which triggers creation of InteractiveWatchFaceClient.
-        createEngine()
-
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject()
 
         try {
             val watchFaceColorsLatch = CountDownLatch(1)
@@ -1869,25 +1048,12 @@
             TestWatchFaceServiceWithPreviewImageUpdateRequest(context, surfaceHolder)
         var lastPreviewImageUpdateRequestedId = ""
 
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            service.getOrCreateInteractiveWatchFaceClient(
-                "wfId-1",
-                deviceConfig,
-                systemState,
-                null,
-                complications,
-                { runnable -> runnable.run() },
-                { lastPreviewImageUpdateRequestedId = it }
-            )
-        }
-
-        // Create the engine which triggers creation of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
-
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(
+            watchFaceService = wallpaperService,
+            instanceId = "wfId-1",
+            previewExecutor = { runnable -> runnable.run() },
+            previewListener = { lastPreviewImageUpdateRequestedId = it }
+        )
 
         assertTrue(
             wallpaperService.rendererInitializedLatch.await(
@@ -1909,23 +1075,8 @@
             context,
             surfaceHolder
         )
-        val deferredInteractiveInstance = handlerCoroutineScope.async {
-            @Suppress("deprecation")
-            service.getOrCreateInteractiveWatchFaceClient(
-                "testId",
-                deviceConfig,
-                systemState,
-                null,
-                complications
-            )
-        }
-        // Create the engine which triggers construction of the interactive instance.
-        handler.post {
-            engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
-        }
 
-        // Wait for the instance to be created.
-        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService)
 
         var lastDisconnectReason = 0
         interactiveInstance.addClientDisconnectListener(
@@ -1933,9 +1084,8 @@
                 override fun onClientDisconnected(@DisconnectReason disconnectReason: Int) {
                     lastDisconnectReason = disconnectReason
                 }
-            },
-            { it.run() }
-        )
+            }
+        ) { it.run() }
 
         // Simulate detach.
         engine.onDestroy()
@@ -1944,665 +1094,232 @@
     }
 }
 
-internal class TestExampleCanvasAnalogWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder
-) : ExampleCanvasAnalogWatchFaceService() {
-    internal lateinit var watchFace: WatchFace
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+class WatchFaceControlClientScreenshotTest : WatchFaceControlClientTestBase() {
+    @get:Rule
+    val screenshotRule: AndroidXScreenshotTestRule =
+        AndroidXScreenshotTestRule("wear/wear-watchface-client")
 
-    init {
-        attachBaseContext(testContext)
-    }
+    private val exampleOpenGLWatchFaceComponentName =
+        componentOf<ExampleOpenGLBackgroundInitWatchFaceService>()
 
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun getOrCreateInteractiveWatchFaceClient() {
+        val interactiveInstance = getOrCreateTestSubject()
 
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): WatchFace {
-        watchFace = super.createWatchFace(
-            surfaceHolder,
-            watchState,
-            complicationSlotsManager,
-            currentUserStyleRepository
-        )
-        return watchFace
-    }
-}
-
-internal class TestExampleOpenGLBackgroundInitWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder
-) : ExampleOpenGLBackgroundInitWatchFaceService() {
-    internal lateinit var watchFace: WatchFace
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): WatchFace {
-        watchFace = super.createWatchFace(
-            surfaceHolder,
-            watchState,
-            complicationSlotsManager,
-            currentUserStyleRepository
-        )
-        return watchFace
-    }
-}
-
-internal open class TestCrashingWatchFaceService : WatchFaceService() {
-
-    companion object {
-        const val COMPLICATION_ID = 123
-    }
-
-    override fun createComplicationSlotsManager(
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): ComplicationSlotsManager {
-        return ComplicationSlotsManager(
-            listOf(
-                ComplicationSlot.createRoundRectComplicationSlotBuilder(
-                    COMPLICATION_ID,
-                    { _, _ -> throw Exception("Deliberately crashing") },
-                    listOf(ComplicationType.LONG_TEXT),
-                    DefaultComplicationDataSourcePolicy(
-                        SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET,
-                        ComplicationType.LONG_TEXT
-                    ),
-                    ComplicationSlotBounds(RectF(0.1f, 0.1f, 0.4f, 0.4f))
-                ).build()
+        val bitmap = interactiveInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
             ),
-            currentUserStyleRepository
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
         )
-    }
 
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): WatchFace {
-        throw Exception("Deliberately crashing")
-    }
-}
-
-internal class TestWatchfaceOverlayStyleWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder,
-    private var watchFaceOverlayStyle: WatchFace.OverlayStyle
-) : WatchFaceService() {
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.DIGITAL,
-        @Suppress("deprecation")
-        object : Renderer.CanvasRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            CanvasType.HARDWARE,
-            16
-        ) {
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
-                // Actually rendering something isn't required.
-            }
-
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
-                // Actually rendering something isn't required.
-            }
+        try {
+            bitmap.assertAgainstGolden(screenshotRule, "interactiveScreenshot")
+        } finally {
+            interactiveInstance.close()
         }
-    ).setOverlayStyle(watchFaceOverlayStyle)
-}
-
-internal class TestAsyncCanvasRenderInitWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder,
-    private var initCompletableDeferred: CompletableDeferred<Unit>
-) : WatchFaceService() {
-
-    init {
-        attachBaseContext(testContext)
     }
 
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.DIGITAL,
-        @Suppress("deprecation")
-        object : Renderer.CanvasRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            CanvasType.HARDWARE,
-            16
-        ) {
-            override suspend fun init() {
-                initCompletableDeferred.await()
-            }
-
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
-                // Actually rendering something isn't required.
-            }
-
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
-                TODO("Not yet implemented")
-            }
-        }
-    )
-
-    override fun getSystemTimeProvider() = object : SystemTimeProvider {
-        override fun getSystemTimeMillis() = 123456789L
-
-        override fun getSystemTimeZoneId() = ZoneId.of("UTC")
-    }
-}
-
-internal class TestAsyncGlesRenderInitWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder,
-    private var onUiThreadGlSurfaceCreatedCompletableDeferred: CompletableDeferred<Unit>,
-    private var onBackgroundThreadGlContextCreatedCompletableDeferred: CompletableDeferred<Unit>
-) : WatchFaceService() {
-    internal lateinit var watchFace: WatchFace
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.DIGITAL,
-        @Suppress("deprecation")
-        object : Renderer.GlesRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            16
-        ) {
-            override suspend fun onUiThreadGlSurfaceCreated(width: Int, height: Int) {
-                onUiThreadGlSurfaceCreatedCompletableDeferred.await()
-            }
-
-            override suspend fun onBackgroundThreadGlContextCreated() {
-                onBackgroundThreadGlContextCreatedCompletableDeferred.await()
-            }
-
-            override fun render(zonedDateTime: ZonedDateTime) {
-                // GLES rendering is complicated and not strictly necessary for our test.
-            }
-
-            override fun renderHighlightLayer(zonedDateTime: ZonedDateTime) {
-                TODO("Not yet implemented")
-            }
-        }
-    )
-}
-
-internal class TestComplicationProviderDefaultsWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder
-) : WatchFaceService() {
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    override fun createComplicationSlotsManager(
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): ComplicationSlotsManager {
-        return ComplicationSlotsManager(
-            listOf(
-                ComplicationSlot.createRoundRectComplicationSlotBuilder(
-                    123,
-                    { _, _ ->
-                        object : CanvasComplication {
-                            override fun render(
-                                canvas: Canvas,
-                                bounds: Rect,
-                                zonedDateTime: ZonedDateTime,
-                                renderParameters: RenderParameters,
-                                slotId: Int
-                            ) {
-                            }
-
-                            override fun drawHighlight(
-                                canvas: Canvas,
-                                bounds: Rect,
-                                boundsType: Int,
-                                zonedDateTime: ZonedDateTime,
-                                color: Int
-                            ) {
-                            }
-
-                            override fun getData() = NoDataComplicationData()
-
-                            override fun loadData(
-                                complicationData: ComplicationData,
-                                loadDrawablesAsynchronous: Boolean
-                            ) {
-                            }
-                        }
-                    },
-                    listOf(
-                        ComplicationType.PHOTO_IMAGE,
-                        ComplicationType.LONG_TEXT,
-                        ComplicationType.SHORT_TEXT
-                    ),
-                    DefaultComplicationDataSourcePolicy(
-                        ComponentName("com.package1", "com.app1"),
-                        ComplicationType.PHOTO_IMAGE,
-                        ComponentName("com.package2", "com.app2"),
-                        ComplicationType.LONG_TEXT,
-                        SystemDataSources.DATA_SOURCE_STEP_COUNT,
-                        ComplicationType.SHORT_TEXT
-                    ),
-                    ComplicationSlotBounds(
-                        RectF(0.1f, 0.2f, 0.3f, 0.4f)
-                    )
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun getOrCreateInteractiveWatchFaceClient_initialStyle() {
+        val interactiveInstance = getOrCreateTestSubject(
+            // An incomplete map which is OK.
+            userStyle = UserStyleData(
+                mapOf(
+                    "color_style_setting" to "green_style".encodeToByteArray(),
+                    "draw_hour_pips_style_setting" to BooleanOption.FALSE.id.value,
+                    "watch_hand_length_style_setting" to DoubleRangeOption(0.8).id.value
                 )
-                    .build()
-            ),
-            currentUserStyleRepository
+            )
         )
-    }
 
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.DIGITAL,
-        @Suppress("deprecation")
-        object : Renderer.CanvasRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            CanvasType.HARDWARE,
-            16
-        ) {
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
-
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
-            }
-        }
-    )
-}
-
-internal class TestEdgeComplicationWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder
-) : WatchFaceService() {
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    @OptIn(ComplicationExperimental::class)
-    override fun createComplicationSlotsManager(
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): ComplicationSlotsManager {
-        return ComplicationSlotsManager(
-            listOf(
-                ComplicationSlot.createEdgeComplicationSlotBuilder(
-                    123,
-                    { _, _ ->
-                        object : CanvasComplication {
-                            override fun render(
-                                canvas: Canvas,
-                                bounds: Rect,
-                                zonedDateTime: ZonedDateTime,
-                                renderParameters: RenderParameters,
-                                slotId: Int
-                            ) {
-                            }
-
-                            override fun drawHighlight(
-                                canvas: Canvas,
-                                bounds: Rect,
-                                boundsType: Int,
-                                zonedDateTime: ZonedDateTime,
-                                color: Int
-                            ) {
-                            }
-
-                            override fun getData() = NoDataComplicationData()
-
-                            override fun loadData(
-                                complicationData: ComplicationData,
-                                loadDrawablesAsynchronous: Boolean
-                            ) {
-                            }
-                        }
-                    },
-                    listOf(
-                        ComplicationType.PHOTO_IMAGE,
-                        ComplicationType.LONG_TEXT,
-                        ComplicationType.SHORT_TEXT
-                    ),
-                    DefaultComplicationDataSourcePolicy(
-                        ComponentName("com.package1", "com.app1"),
-                        ComplicationType.PHOTO_IMAGE,
-                        ComponentName("com.package2", "com.app2"),
-                        ComplicationType.LONG_TEXT,
-                        SystemDataSources.DATA_SOURCE_STEP_COUNT,
-                        ComplicationType.SHORT_TEXT
-                    ),
-                    ComplicationSlotBounds(
-                        RectF(0f, 0f, 1f, 1f)
-                    ),
-                    BoundingArc(45f, 90f, 0.1f)
-                )
-                    .build()
+        val bitmap = interactiveInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
             ),
-            currentUserStyleRepository
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
         )
-    }
 
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.DIGITAL,
-        @Suppress("deprecation")
-        object : Renderer.CanvasRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            CanvasType.HARDWARE,
-            16
-        ) {
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
-
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
-            }
+        try {
+            bitmap.assertAgainstGolden(screenshotRule, "initialStyle")
+        } finally {
+            interactiveInstance.close()
         }
-    )
-}
-
-internal class TestLifeCycleWatchFaceService : WatchFaceService() {
-    companion object {
-        val lifeCycleEvents = ArrayList<String>()
     }
 
-    override fun onCreate() {
-        super.onCreate()
-        lifeCycleEvents.add("WatchFaceService.onCreate")
-    }
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun getOrCreateInteractiveWatchFaceClient_existingOpenInstance_styleChange() {
+        val watchFaceService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
 
-    override fun onDestroy() {
-        super.onDestroy()
-        lifeCycleEvents.add("WatchFaceService.onDestroy")
-    }
+        val testId = "testId"
 
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.DIGITAL,
-        @Suppress("deprecation")
-        object : Renderer.GlesRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            16
-        ) {
-            init {
-                lifeCycleEvents.add("Renderer.constructed")
-            }
+        getOrCreateTestSubject(watchFaceService, instanceId = testId)
 
-            override fun onDestroy() {
-                super.onDestroy()
-                lifeCycleEvents.add("Renderer.onDestroy")
-            }
-
-            override fun render(zonedDateTime: ZonedDateTime) {}
-
-            override fun renderHighlightLayer(zonedDateTime: ZonedDateTime) {}
-        }
-    )
-}
-
-internal class TestWatchFaceServiceWithPreviewImageUpdateRequest(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder,
-) : WatchFaceService() {
-    val rendererInitializedLatch = CountDownLatch(1)
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    @Suppress("deprecation")
-    private lateinit var renderer: Renderer.CanvasRenderer
-
-    fun triggerPreviewImageUpdateRequest() {
-        renderer.sendPreviewImageNeedsUpdateRequest()
-    }
-
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): WatchFace {
-        @Suppress("deprecation")
-        renderer = object : Renderer.CanvasRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            CanvasType.HARDWARE,
-            16
-        ) {
-            override suspend fun init() {
-                rendererInitializedLatch.countDown()
-            }
-
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
-
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
-            }
-        }
-        return WatchFace(WatchFaceType.DIGITAL, renderer)
-    }
-}
-
-internal class TestComplicationStyleUpdateWatchFaceService(
-    testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder
-) : WatchFaceService() {
-
-    init {
-        attachBaseContext(testContext)
-    }
-
-    @Suppress("deprecation")
-    private val complicationsStyleSetting =
-        UserStyleSetting.ComplicationSlotsUserStyleSetting(
-            UserStyleSetting.Id(COMPLICATIONS_STYLE_SETTING),
-            resources,
-            R.string.watchface_complications_setting,
-            R.string.watchface_complications_setting_description,
-            icon = null,
-            complicationConfig = listOf(
-                UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
-                    UserStyleSetting.Option.Id(NO_COMPLICATIONS),
-                    resources,
-                    R.string.watchface_complications_setting_none,
-                    null,
-                    listOf(
-                        UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
-                            123,
-                            enabled = false
-                        )
+        val deferredInteractiveInstance2 = handlerCoroutineScope.async {
+            @Suppress("deprecation")
+            service.getOrCreateInteractiveWatchFaceClient(
+                testId,
+                deviceConfig,
+                systemState,
+                UserStyleData(
+                    mapOf(
+                        "color_style_setting" to "blue_style".encodeToByteArray(),
+                        "draw_hour_pips_style_setting" to BooleanOption.FALSE.id.value,
+                        "watch_hand_length_style_setting" to DoubleRangeOption(0.25).id.value
                     )
                 ),
-                UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
-                    UserStyleSetting.Option.Id(LEFT_COMPLICATION),
-                    resources,
-                    R.string.watchface_complications_setting_left,
-                    null,
-                    listOf(
-                        UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
-                            123,
-                            enabled = true,
-                            nameResourceId = R.string.left_complication_screen_name,
-                            screenReaderNameResourceId =
-                            R.string.left_complication_screen_reader_name
-                        )
-                    )
-                )
+                complications
+            )
+        }
+
+        val interactiveInstance2 = awaitWithTimeout(deferredInteractiveInstance2)
+        assertThat(interactiveInstance2.instanceId).isEqualTo("testId")
+
+        val bitmap = interactiveInstance2.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
             ),
-            listOf(WatchFaceLayer.COMPLICATIONS)
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
         )
 
-    override fun createUserStyleSchema(): UserStyleSchema =
-        UserStyleSchema(listOf(complicationsStyleSetting))
-
-    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
-
-    override fun createComplicationSlotsManager(
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ): ComplicationSlotsManager {
-        return ComplicationSlotsManager(
-            listOf(
-                ComplicationSlot.createRoundRectComplicationSlotBuilder(
-                    123,
-                    { _, _ ->
-                        object : CanvasComplication {
-                            override fun render(
-                                canvas: Canvas,
-                                bounds: Rect,
-                                zonedDateTime: ZonedDateTime,
-                                renderParameters: RenderParameters,
-                                slotId: Int
-                            ) {
-                            }
-
-                            override fun drawHighlight(
-                                canvas: Canvas,
-                                bounds: Rect,
-                                boundsType: Int,
-                                zonedDateTime: ZonedDateTime,
-                                color: Int
-                            ) {
-                            }
-
-                            override fun getData() = NoDataComplicationData()
-
-                            override fun loadData(
-                                complicationData: ComplicationData,
-                                loadDrawablesAsynchronous: Boolean
-                            ) {
-                            }
-                        }
-                    },
-                    listOf(
-                        ComplicationType.PHOTO_IMAGE,
-                        ComplicationType.LONG_TEXT,
-                        ComplicationType.SHORT_TEXT
-                    ),
-                    DefaultComplicationDataSourcePolicy(
-                        ComponentName("com.package1", "com.app1"),
-                        ComplicationType.PHOTO_IMAGE,
-                        ComponentName("com.package2", "com.app2"),
-                        ComplicationType.LONG_TEXT,
-                        SystemDataSources.DATA_SOURCE_STEP_COUNT,
-                        ComplicationType.SHORT_TEXT
-                    ),
-                    ComplicationSlotBounds(
-                        RectF(0.1f, 0.2f, 0.3f, 0.4f)
-                    )
-                ).build()
-            ),
-            currentUserStyleRepository
-        )
+        try {
+            // Note the hour hand pips and both complicationSlots should be visible in this image.
+            bitmap.assertAgainstGolden(screenshotRule, "existingOpenInstance_styleChange")
+        } finally {
+            interactiveInstance2.close()
+        }
     }
 
-    override suspend fun createWatchFace(
-        surfaceHolder: SurfaceHolder,
-        watchState: WatchState,
-        complicationSlotsManager: ComplicationSlotsManager,
-        currentUserStyleRepository: CurrentUserStyleRepository
-    ) = WatchFace(
-        WatchFaceType.ANALOG,
-        @Suppress("deprecation")
-        object : Renderer.CanvasRenderer(
-            surfaceHolder,
-            currentUserStyleRepository,
-            watchState,
-            CanvasType.HARDWARE,
-            16
-        ) {
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+    @SuppressLint("NewApi") // renderWatchFaceToBitmap
+    @Test
+    fun updateInstance() {
+        val interactiveInstance = getOrCreateTestSubject(
+            userStyle = UserStyleData(
+                mapOf(
+                    COLOR_STYLE_SETTING to GREEN_STYLE.encodeToByteArray(),
+                    WATCH_HAND_LENGTH_STYLE_SETTING to DoubleRangeOption(0.25).id.value,
+                    DRAW_HOUR_PIPS_STYLE_SETTING to BooleanOption.FALSE.id.value,
+                    COMPLICATIONS_STYLE_SETTING to NO_COMPLICATIONS.encodeToByteArray()
+                )
+            )
+        )
 
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
-            }
+        assertThat(interactiveInstance.instanceId).isEqualTo("testId")
+
+        // Note this map doesn't include all the categories, which is fine the others will be set
+        // to their defaults.
+        interactiveInstance.updateWatchFaceInstance(
+            "testId2",
+            UserStyleData(
+                mapOf(
+                    COLOR_STYLE_SETTING to BLUE_STYLE.encodeToByteArray(),
+                    WATCH_HAND_LENGTH_STYLE_SETTING to DoubleRangeOption(0.9).id.value,
+                )
+            )
+        )
+
+        assertThat(interactiveInstance.instanceId).isEqualTo("testId2")
+
+        // It should be possible to create an instance with the updated id.
+        val instance =
+            service.getInteractiveWatchFaceClientInstance("testId2")
+        assertThat(instance).isNotNull()
+        instance?.close()
+
+        // The previous instance should still be usable despite the new instance being closed.
+        interactiveInstance.updateComplicationData(complications)
+        val bitmap = interactiveInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
+            ),
+            Instant.ofEpochMilli(1234567),
+            null,
+            complications
+        )
+
+        try {
+            // Note the hour hand pips and both complicationSlots should be visible in this image.
+            bitmap.assertAgainstGolden(screenshotRule, "setUserStyle")
+        } finally {
+            interactiveInstance.close()
         }
-    )
+    }
+
+    @Ignore // b/225230182
+    @Test
+    fun interactiveAndHeadlessOpenGlWatchFaceInstances() {
+        val surfaceTexture = SurfaceTexture(false)
+        surfaceTexture.setDefaultBufferSize(400, 400)
+        Mockito.`when`(surfaceHolder2.surface).thenReturn(Surface(surfaceTexture))
+        Mockito.`when`(surfaceHolder2.surfaceFrame)
+            .thenReturn(Rect(0, 0, 400, 400))
+
+        val wallpaperService =
+            TestExampleOpenGLBackgroundInitWatchFaceService(context, surfaceHolder2)
+
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService,
+            complications = emptyMap()
+        )
+
+        val headlessInstance = HeadlessWatchFaceClient.createFromBundle(
+            service.createHeadlessWatchFaceClient(
+                "id",
+                exampleOpenGLWatchFaceComponentName,
+                deviceConfig,
+                200,
+                200
+            )!!.toBundle()
+        )
+
+        // Take screenshots from both instances to confirm rendering works as expected despite the
+        // watch face using shared SharedAssets.
+        val interactiveBitmap = interactiveInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
+            ),
+            Instant.ofEpochMilli(1234567),
+            null,
+            null
+        )
+
+        interactiveBitmap.assertAgainstGolden(screenshotRule, "opengl_interactive")
+
+        val headlessBitmap = headlessInstance.renderWatchFaceToBitmap(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                WatchFaceLayer.ALL_WATCH_FACE_LAYERS,
+                null
+            ),
+            Instant.ofEpochMilli(1234567),
+            null,
+            null
+        )
+
+        headlessBitmap.assertAgainstGolden(screenshotRule, "opengl_headless")
+
+        headlessInstance.close()
+        interactiveInstance.close()
+    }
 }
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
deleted file mode 100644
index 5c954de..0000000
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
+++ /dev/null
@@ -1,2553 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.wearable.complications;
-
-import android.annotation.SuppressLint;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.BadParcelableException;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies;
-import androidx.wear.watchface.complications.data.ComplicationDisplayPolicy;
-import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies;
-import androidx.wear.watchface.complications.data.ComplicationPersistencePolicy;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Container for complication data of all types.
- *
- * <p>A {@link androidx.wear.watchface.complications.ComplicationProviderService} should create
- * instances of
- * this class using {@link ComplicationData.Builder} and send them to the complication system in
- * response to
- * {@link androidx.wear.watchface.complications.ComplicationProviderService#onComplicationRequest}.
- * Depending on the type of complication data, some fields will be required and some will be
- * optional - see the documentation for each type, and for the builder's set methods, for details.
- *
- * <p>A watch face will receive instances of this class as long as providers are configured.
- *
- * <p>When rendering the complication data for a given time, the watch face should first call {@link
- * #isActiveAt} to determine whether the data is valid at that time. See the documentation for each
- * of the complication types below for details of which fields are expected to be displayed.
- *
- * @hide
- */
-@SuppressLint("BanParcelableUsage")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class ComplicationData implements Parcelable, Serializable {
-
-    private static final String TAG = "ComplicationData";
-    public static final String PLACEHOLDER_STRING = "__placeholder__";
-
-    /** @hide */
-    @IntDef({
-            TYPE_EMPTY,
-            TYPE_NOT_CONFIGURED,
-            TYPE_SHORT_TEXT,
-            TYPE_LONG_TEXT,
-            TYPE_RANGED_VALUE,
-            TYPE_ICON,
-            TYPE_SMALL_IMAGE,
-            TYPE_LARGE_IMAGE,
-            TYPE_NO_PERMISSION,
-            TYPE_NO_DATA,
-            TYPE_GOAL_PROGRESS,
-            TYPE_WEIGHTED_ELEMENTS,
-            EXP_TYPE_PROTO_LAYOUT,
-            EXP_TYPE_LIST
-    })
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ComplicationType {
-    }
-
-    /**
-     * Type sent when a complication does not have a provider configured. The system will send data
-     * of this type to watch faces when the user has not chosen a provider for an active
-     * complication, and the watch face has not set a default provider. Providers cannot send data
-     * of this type.
-     *
-     * <p>No fields may be populated for complication data of this type.
-     */
-    public static final int TYPE_NOT_CONFIGURED = 1;
-
-    /**
-     * Type sent when the user has specified that an active complication should have no provider,
-     * i.e. when the user has chosen "Empty" in the provider chooser. Providers cannot send data of
-     * this type.
-     *
-     * <p>No fields may be populated for complication data of this type.
-     */
-    public static final int TYPE_EMPTY = 2;
-
-    /**
-     * Type that can be sent by any provider, regardless of the configured type, when the provider
-     * has no data to be displayed. Watch faces may choose whether to render this in some way or
-     * leave the slot empty.
-     *
-     * <p>No fields may be populated for complication data of this type.
-     */
-    public static final int TYPE_NO_DATA = 10;
-
-    /**
-     * Type used for complications where the primary piece of data is a short piece of text
-     * (expected to be no more than seven characters in length). The short text may be accompanied
-     * by an icon or a short title (or both, but if both are provided then a watch face may choose
-     * to display only one).
-     *
-     * <p>The <i>short text</i> field is required for this type, and is expected to always be
-     * displayed.
-     *
-     * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>) and <i>short title</i> fields are
-     * optional for this type. If only one of these is provided, it is expected that it will be
-     * displayed. If both are provided, it is expected that one of these will be displayed.
-     */
-    public static final int TYPE_SHORT_TEXT = 3;
-
-    /**
-     * Type used for complications where the primary piece of data is a piece of text. The text may
-     * be accompanied by an icon and/or a title.
-     *
-     * <p>The <i>long text</i> field is required for this type, and is expected to always be
-     * displayed.
-     *
-     * <p>The <i>long title</i> field is optional for this type. If provided, it is expected that
-     * this field will be displayed.
-     *
-     * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>) and <i>small image</i> fields are also
-     * optional for this type. If provided, at least one of these should be displayed.
-     */
-    public static final int TYPE_LONG_TEXT = 4;
-
-    /**
-     * Type used for complications including a numerical value within a range, such as a percentage.
-     * The value may be accompanied by an icon and/or short text and title.
-     *
-     * <p>The <i>value</i>, <i>min value</i>, and <i>max value</i> fields are required for this
-     * type, and the value within the range is expected to always be displayed.
-     *
-     * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>), <i>short title</i>, and <i>short
-     * text</i> fields are optional for this type, but at least one must be defined. The watch face
-     * may choose which of these fields to display, if any.
-     */
-    public static final int TYPE_RANGED_VALUE = 5;
-
-    /**
-     * Type used for complications which consist only of a tintable icon.
-     *
-     * <p>The <i>icon</i> field is required for this type, and is expected to always be displayed,
-     * unless the device is in ambient mode with burn-in protection enabled, in which case the
-     * <i>burnInProtectionIcon</i> field should be used instead.
-     *
-     * <p>The contentDescription field is recommended for this type. Use it to describe what data
-     * the icon represents. If the icon is purely stylistic, and does not convey any information to
-     * the user, then enter the empty string as the contentDescription.
-     *
-     * <p>No other fields are valid for this type.
-     */
-    public static final int TYPE_ICON = 6;
-
-    /**
-     * Type used for complications which consist only of a small image.
-     *
-     * <p>The <i>small image</i> field is required for this type, and is expected to always be
-     * displayed, unless the device is in ambient mode, in which case either nothing or the
-     * <i>burnInProtectionSmallImage</i> field may be used instead.
-     *
-     * <p>The contentDescription field is recommended for this type. Use it to describe what data
-     * the image represents. If the image is purely stylistic, and does not convey any information
-     * to the user, then enter the empty string as the contentDescription.
-     *
-     * <p>No other fields are valid for this type.
-     */
-    public static final int TYPE_SMALL_IMAGE = 7;
-
-    /**
-     * Type used for complications which consist only of a large image. A large image here is one
-     * that could be used to fill the watch face, for example as the background.
-     *
-     * <p>The <i>large image</i> field is required for this type, and is expected to always be
-     * displayed, unless the device is in ambient mode.
-     *
-     * <p>The contentDescription field is recommended for this type. Use it to describe what data
-     * the image represents. If the image is purely stylistic, and does not convey any information
-     * to the user, then enter the empty string as the contentDescription.
-     *
-     * <p>No other fields are valid for this type.
-     */
-    public static final int TYPE_LARGE_IMAGE = 8;
-
-    /**
-     * Type sent by the system when the watch face does not have permission to receive complication
-     * data.
-     *
-     * <p>Fields will be populated to allow the data to be rendered as if it were of {@link
-     * #TYPE_SHORT_TEXT} or {@link #TYPE_ICON} for consistency and convenience, but watch faces may
-     * render this as they see fit.
-     *
-     * <p>It is recommended that, where possible, tapping on the complication when in this state
-     * should trigger a permission request.
-     */
-    public static final int TYPE_NO_PERMISSION = 9;
-
-    /**
-     * Type used for complications which indicate progress towards a goal. The value may be
-     * accompanied by an icon and/or short text and title.
-     *
-     * <p>The <i>value</i>, and <i>target value</i> fields are required for this type, and the
-     * value is expected to always be displayed. The value must be >= 0 and may be > target value.
-     * E.g. 15000 out of a target of 10000 steps.
-     *
-     * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>), <i>short title</i>, and <i>short
-     * text</i> fields are optional for this type, but at least one must be defined. The watch face
-     * may choose which of these fields to display, if any.
-     */
-    public static final int TYPE_GOAL_PROGRESS = 13;
-
-    /**
-     * Type used for complications to display a series of weighted values e.g. in a pie chart. The
-     * weighted values may be accompanied by an icon and/or short text and title.
-     *
-     * <p>The <i>element weights</i> and <i>element colors</i> fields are required for this type,
-     * and the value within the range is expected to always be displayed.
-     *
-     * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>), <i>short title</i>, and <i>short
-     * text</i> fields are optional for this type, but at least one must be defined. The watch face
-     * may choose which of these fields to display, if any.
-     */
-    public static final int TYPE_WEIGHTED_ELEMENTS = 14;
-
-    // The following types are experimental, and they have negative IDs.
-
-    /** Type that specifies a proto layout based complication. */
-    public static final int EXP_TYPE_PROTO_LAYOUT = -11;
-
-    /** Type that specifies a list of complication values. E.g. to support linear 3. */
-    public static final int EXP_TYPE_LIST = -12;
-
-    /** @hide */
-    @IntDef({IMAGE_STYLE_PHOTO, IMAGE_STYLE_ICON})
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImageStyle {
-    }
-
-    /**
-     * Style for small images which are photos that are expected to fill the space available. Images
-     * of this style may be cropped to fit the shape of the complication - in particular, the image
-     * may be cropped to a circle. Photos my not be recolored.
-     *
-     * <p>This is the default value.
-     */
-    public static final int IMAGE_STYLE_PHOTO = 1;
-
-    /**
-     * Style for small images that have a transparent background and are expected to be drawn
-     * entirely within the space available, such as a launcher icon. Watch faces may add padding
-     * when drawing these images, but should never crop these images. Icons may be recolored to fit
-     * the complication style.
-     */
-    public static final int IMAGE_STYLE_ICON = 2;
-
-    private static final String FIELD_COLOR_RAMP = "COLOR_RAMP";
-    private static final String FIELD_COLOR_RAMP_INTERPOLATED = "COLOR_RAMP_INTERPOLATED";
-    private static final String FIELD_DATA_SOURCE = "FIELD_DATA_SOURCE";
-    private static final String FIELD_DISPLAY_POLICY = "DISPLAY_POLICY";
-    private static final String FIELD_ELEMENT_BACKGROUND_COLOR = "ELEMENT_BACKGROUND_COLOR";
-    private static final String FIELD_ELEMENT_COLORS = "ELEMENT_COLORS";
-    private static final String FIELD_ELEMENT_WEIGHTS = "ELEMENT_WEIGHTS";
-    private static final String FIELD_END_TIME = "END_TIME";
-    private static final String FIELD_ICON = "ICON";
-    private static final String FIELD_ICON_BURN_IN_PROTECTION = "ICON_BURN_IN_PROTECTION";
-    private static final String FIELD_IMAGE_STYLE = "IMAGE_STYLE";
-    private static final String FIELD_LARGE_IMAGE = "LARGE_IMAGE";
-    private static final String FIELD_LONG_TITLE = "LONG_TITLE";
-    private static final String FIELD_LONG_TEXT = "LONG_TEXT";
-    private static final String FIELD_MAX_VALUE = "MAX_VALUE";
-    private static final String FIELD_MIN_VALUE = "MIN_VALUE";
-    private static final String FIELD_PERSISTENCE_POLICY = "PERSISTENCE_POLICY";
-    private static final String FIELD_PLACEHOLDER_FIELDS = "PLACEHOLDER_FIELDS";
-    private static final String FIELD_PLACEHOLDER_TYPE = "PLACEHOLDER_TYPE";
-    private static final String FIELD_SMALL_IMAGE = "SMALL_IMAGE";
-    private static final String FIELD_SMALL_IMAGE_BURN_IN_PROTECTION =
-            "SMALL_IMAGE_BURN_IN_PROTECTION";
-    private static final String FIELD_SHORT_TITLE = "SHORT_TITLE";
-    private static final String FIELD_SHORT_TEXT = "SHORT_TEXT";
-    private static final String FIELD_START_TIME = "START_TIME";
-    private static final String FIELD_TAP_ACTION = "TAP_ACTION";
-    private static final String FIELD_TAP_ACTION_LOST = "FIELD_TAP_ACTION_LOST";
-    private static final String FIELD_TARGET_VALUE = "TARGET_VALUE";
-    private static final String FIELD_TIMELINE_START_TIME = "TIMELINE_START_TIME";
-    private static final String FIELD_TIMELINE_END_TIME = "TIMELINE_END_TIME";
-    private static final String FIELD_TIMELINE_ENTRIES = "TIMELINE";
-    private static final String FIELD_TIMELINE_ENTRY_TYPE = "TIMELINE_ENTRY_TYPE";
-    private static final String FIELD_VALUE = "VALUE";
-    private static final String FIELD_VALUE_TYPE = "VALUE_TYPE";
-
-    // Experimental fields, these are subject to change without notice.
-    private static final String EXP_FIELD_LIST_ENTRIES = "EXP_LIST_ENTRIES";
-    private static final String EXP_FIELD_LIST_ENTRY_TYPE = "EXP_LIST_ENTRY_TYPE";
-    private static final String EXP_FIELD_LIST_STYLE_HINT = "EXP_LIST_STYLE_HINT";
-    private static final String EXP_FIELD_PROTO_LAYOUT_AMBIENT = "EXP_FIELD_PROTO_LAYOUT_AMBIENT";
-    private static final String EXP_FIELD_PROTO_LAYOUT_INTERACTIVE =
-            "EXP_FIELD_PROTO_LAYOUT_INTERACTIVE";
-    private static final String EXP_FIELD_PROTO_LAYOUT_RESOURCES =
-            "EXP_FIELD_PROTO_LAYOUT_RESOURCES";
-
-    // Originally it was planned to support both content and image content descriptions.
-    private static final String FIELD_CONTENT_DESCRIPTION = "IMAGE_CONTENT_DESCRIPTION";
-
-    // The set of valid types.
-    private static final Set<Integer> VALID_TYPES = validTypes();
-
-    private static Set<Integer> validTypes() {
-        HashSet<Integer> set = new HashSet<>();
-        set.add(TYPE_NOT_CONFIGURED);
-        set.add(TYPE_EMPTY);
-        set.add(TYPE_SHORT_TEXT);
-        set.add(TYPE_LONG_TEXT);
-        set.add(TYPE_RANGED_VALUE);
-        set.add(TYPE_ICON);
-        set.add(TYPE_SMALL_IMAGE);
-        set.add(TYPE_LARGE_IMAGE);
-        set.add(TYPE_NO_PERMISSION);
-        set.add(TYPE_NO_DATA);
-        set.add(EXP_TYPE_PROTO_LAYOUT);
-        set.add(EXP_TYPE_LIST);
-        set.add(TYPE_GOAL_PROGRESS);
-        set.add(TYPE_WEIGHTED_ELEMENTS);
-        return set;
-    }
-
-    // Used for validation. REQUIRED_FIELDS[i] is an array containing all the fields which must be
-    // populated for @ComplicationType i.
-    private static final Map<Integer, String[]> REQUIRED_FIELDS = requiredFieldsMap();
-
-    private static Map<Integer, String[]> requiredFieldsMap() {
-        HashMap<Integer, String[]> map = new HashMap<>();
-        map.put(TYPE_NOT_CONFIGURED, new String[0]);
-        map.put(TYPE_EMPTY, new String[0]);
-        map.put(TYPE_SHORT_TEXT, new String[]{FIELD_SHORT_TEXT});
-        map.put(TYPE_LONG_TEXT, new String[]{FIELD_LONG_TEXT});
-        map.put(TYPE_RANGED_VALUE, new String[]{FIELD_VALUE, FIELD_MIN_VALUE, FIELD_MAX_VALUE});
-        map.put(TYPE_ICON, new String[]{FIELD_ICON});
-        map.put(TYPE_SMALL_IMAGE, new String[]{FIELD_SMALL_IMAGE, FIELD_IMAGE_STYLE});
-        map.put(TYPE_LARGE_IMAGE, new String[]{FIELD_LARGE_IMAGE});
-        map.put(TYPE_NO_PERMISSION, new String[0]);
-        map.put(TYPE_NO_DATA, new String[0]);
-        map.put(EXP_TYPE_PROTO_LAYOUT, new String[]{EXP_FIELD_PROTO_LAYOUT_AMBIENT,
-                EXP_FIELD_PROTO_LAYOUT_INTERACTIVE,
-                EXP_FIELD_PROTO_LAYOUT_RESOURCES});
-        map.put(EXP_TYPE_LIST, new String[]{EXP_FIELD_LIST_ENTRIES});
-        map.put(TYPE_GOAL_PROGRESS, new String[]{FIELD_VALUE, FIELD_TARGET_VALUE});
-        map.put(TYPE_WEIGHTED_ELEMENTS, new String[]{FIELD_ELEMENT_WEIGHTS,
-                FIELD_ELEMENT_COLORS,
-                FIELD_ELEMENT_BACKGROUND_COLOR});
-        return map;
-    }
-
-    // Used for validation. OPTIONAL_FIELDS[i] is an array containing all the fields which are
-    // valid but not required for type i.
-    private static final Map<Integer, String[]> OPTIONAL_FIELDS = optionalFieldsMap();
-
-    private static Map<Integer, String[]> optionalFieldsMap() {
-        HashMap<Integer, String[]> map = new HashMap<>();
-        map.put(TYPE_NOT_CONFIGURED, new String[0]);
-        map.put(TYPE_EMPTY, new String[0]);
-        map.put(TYPE_SHORT_TEXT, new String[]{
-                FIELD_SHORT_TITLE,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_LONG_TEXT, new String[]{
-                FIELD_LONG_TITLE,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_RANGED_VALUE, new String[]{
-                FIELD_SHORT_TEXT,
-                FIELD_SHORT_TITLE,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_COLOR_RAMP,
-                FIELD_COLOR_RAMP_INTERPOLATED,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY,
-                FIELD_VALUE_TYPE});
-        map.put(TYPE_ICON, new String[]{
-                FIELD_TAP_ACTION,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_SMALL_IMAGE, new String[]{
-                FIELD_TAP_ACTION,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_LARGE_IMAGE, new String[]{
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_NO_PERMISSION, new String[]{
-                FIELD_SHORT_TEXT,
-                FIELD_SHORT_TITLE,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY
-        });
-        map.put(TYPE_NO_DATA, new String[]{
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_LARGE_IMAGE,
-                FIELD_LONG_TEXT,
-                FIELD_LONG_TITLE,
-                FIELD_MAX_VALUE,
-                FIELD_MIN_VALUE,
-                FIELD_PLACEHOLDER_FIELDS,
-                FIELD_PLACEHOLDER_TYPE,
-                FIELD_SHORT_TEXT,
-                FIELD_SHORT_TITLE,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_TAP_ACTION,
-                FIELD_VALUE,
-                FIELD_VALUE_TYPE,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY
-        });
-        map.put(EXP_TYPE_PROTO_LAYOUT, new String[]{
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(EXP_TYPE_LIST, new String[]{
-                FIELD_TAP_ACTION,
-                EXP_FIELD_LIST_STYLE_HINT,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_GOAL_PROGRESS, new String[]{
-                FIELD_SHORT_TEXT,
-                FIELD_SHORT_TITLE,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_COLOR_RAMP,
-                FIELD_COLOR_RAMP_INTERPOLATED,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        map.put(TYPE_WEIGHTED_ELEMENTS, new String[]{
-                FIELD_SHORT_TEXT,
-                FIELD_SHORT_TITLE,
-                FIELD_ICON,
-                FIELD_ICON_BURN_IN_PROTECTION,
-                FIELD_SMALL_IMAGE,
-                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
-                FIELD_IMAGE_STYLE,
-                FIELD_TAP_ACTION,
-                FIELD_CONTENT_DESCRIPTION,
-                FIELD_DATA_SOURCE,
-                FIELD_PERSISTENCE_POLICY,
-                FIELD_DISPLAY_POLICY});
-        return map;
-    }
-
-    @NonNull
-    public static final Creator<ComplicationData> CREATOR =
-            new Creator<ComplicationData>() {
-                @SuppressLint("SyntheticAccessor")
-                @NonNull
-                @Override
-                public ComplicationData createFromParcel(@NonNull Parcel source) {
-                    return new ComplicationData(source);
-                }
-
-                @NonNull
-                @Override
-                public ComplicationData[] newArray(int size) {
-                    return new ComplicationData[size];
-                }
-            };
-
-    @ComplicationType
-    final int mType;
-    final Bundle mFields;
-
-    ComplicationData(@NonNull Builder builder) {
-        mType = builder.mType;
-        mFields = builder.mFields;
-    }
-
-    ComplicationData(int type, Bundle fields) {
-        mType = type;
-        mFields = fields;
-        mFields.setClassLoader(getClass().getClassLoader());
-    }
-
-    private ComplicationData(@NonNull Parcel in) {
-        mType = in.readInt();
-        mFields = in.readBundle(getClass().getClassLoader());
-    }
-
-    @RequiresApi(api = Build.VERSION_CODES.P)
-    private static class SerializedForm implements Serializable {
-        private static final int VERSION_NUMBER = 19;
-
-        @NonNull
-        ComplicationData mComplicationData;
-
-        SerializedForm() {
-        }
-
-        SerializedForm(@NonNull ComplicationData complicationData) {
-            mComplicationData = complicationData;
-        }
-
-        @SuppressLint("SyntheticAccessor") // For mComplicationData.mFields
-        private void writeObject(ObjectOutputStream oos) throws IOException {
-            oos.writeInt(VERSION_NUMBER);
-            int type = mComplicationData.getType();
-            oos.writeInt(type);
-            oos.writeInt(mComplicationData.getPersistencePolicy());
-            oos.writeInt(mComplicationData.getDisplayPolicy());
-
-            if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
-                oos.writeObject(mComplicationData.getLongText());
-            }
-            if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
-                oos.writeObject(mComplicationData.getLongTitle());
-            }
-            if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
-                oos.writeObject(mComplicationData.getShortText());
-            }
-            if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
-                oos.writeObject(mComplicationData.getShortTitle());
-            }
-            if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
-                oos.writeObject(mComplicationData.getContentDescription());
-            }
-            if (isFieldValidForType(FIELD_ICON, type)) {
-                oos.writeObject(IconSerializableHelper.create(mComplicationData.getIcon()));
-            }
-            if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
-                oos.writeObject(
-                        IconSerializableHelper.create(mComplicationData.getBurnInProtectionIcon()));
-            }
-            if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
-                oos.writeObject(IconSerializableHelper.create(mComplicationData.getSmallImage()));
-            }
-            if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
-                oos.writeObject(IconSerializableHelper.create(
-                        mComplicationData.getBurnInProtectionSmallImage()));
-            }
-            if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
-                oos.writeInt(mComplicationData.getSmallImageStyle());
-            }
-            if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
-                oos.writeObject(IconSerializableHelper.create(mComplicationData.getLargeImage()));
-            }
-            if (isFieldValidForType(FIELD_VALUE, type)) {
-                oos.writeFloat(mComplicationData.getRangedValue());
-            }
-            if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
-                oos.writeInt(mComplicationData.getRangedValueType());
-            }
-            if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
-                oos.writeFloat(mComplicationData.getRangedMinValue());
-            }
-            if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
-                oos.writeFloat(mComplicationData.getRangedMaxValue());
-            }
-            if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
-                oos.writeFloat(mComplicationData.getTargetValue());
-            }
-            if (isFieldValidForType(FIELD_COLOR_RAMP, type)) {
-                int[] colors = mComplicationData.getColorRamp();
-                if (colors != null) {
-                    oos.writeBoolean(true);
-                    oos.writeInt(colors.length);
-                    for (int color : colors) {
-                        oos.writeInt(color);
-                    }
-                } else {
-                    oos.writeBoolean(false);
-                }
-            }
-            if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type)) {
-                Boolean isColorRampSmoothShaded = mComplicationData.isColorRampInterpolated();
-                if (isColorRampSmoothShaded != null) {
-                    oos.writeBoolean(true);
-                    oos.writeBoolean(isColorRampSmoothShaded);
-                } else {
-                    oos.writeBoolean(false);
-                }
-            }
-            if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type)) {
-                float[] weights = mComplicationData.getElementWeights();
-                if (weights != null) {
-                    oos.writeBoolean(true);
-                    oos.writeInt(weights.length);
-                    for (float weight : weights) {
-                        oos.writeFloat(weight);
-                    }
-                } else {
-                    oos.writeBoolean(false);
-                }
-            }
-            if (isFieldValidForType(FIELD_ELEMENT_COLORS, type)) {
-                int[] colors = mComplicationData.getElementColors();
-                if (colors != null) {
-                    oos.writeBoolean(true);
-                    oos.writeInt(colors.length);
-                    for (int color : colors) {
-                        oos.writeInt(color);
-                    }
-                } else {
-                    oos.writeBoolean(false);
-                }
-            }
-            if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
-                oos.writeInt(mComplicationData.getElementBackgroundColor());
-            }
-            if (isFieldValidForType(FIELD_START_TIME, type)) {
-                oos.writeLong(mComplicationData.getStartDateTimeMillis());
-            }
-            if (isFieldValidForType(FIELD_END_TIME, type)) {
-                oos.writeLong(mComplicationData.getEndDateTimeMillis());
-            }
-            oos.writeInt(mComplicationData.mFields.getInt(EXP_FIELD_LIST_ENTRY_TYPE));
-            if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
-                oos.writeInt(mComplicationData.getListStyleHint());
-            }
-            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
-                byte[] bytes = mComplicationData.getInteractiveLayout();
-                if (bytes == null) {
-                    oos.writeInt(0);
-                } else {
-                    oos.writeInt(bytes.length);
-                    oos.write(bytes);
-                }
-            }
-            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
-                byte[] bytes = mComplicationData.getAmbientLayout();
-                if (bytes == null) {
-                    oos.writeInt(0);
-                } else {
-                    oos.writeInt(bytes.length);
-                    oos.write(bytes);
-                }
-            }
-            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
-                byte[] bytes = mComplicationData.getLayoutResources();
-                if (bytes == null) {
-                    oos.writeInt(0);
-                } else {
-                    oos.writeInt(bytes.length);
-                    oos.write(bytes);
-                }
-            }
-            if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
-                ComponentName componentName = mComplicationData.getDataSource();
-                if (componentName == null) {
-                    oos.writeUTF("");
-                } else {
-                    oos.writeUTF(componentName.flattenToString());
-                }
-            }
-
-            // TapAction unfortunately can't be serialized, instead we record if we've lost it.
-            oos.writeBoolean(mComplicationData.hasTapAction()
-                    || mComplicationData.getTapActionLostDueToSerialization());
-            long start = mComplicationData.mFields.getLong(FIELD_TIMELINE_START_TIME, -1);
-            oos.writeLong(start);
-            long end = mComplicationData.mFields.getLong(FIELD_TIMELINE_END_TIME, -1);
-            oos.writeLong(end);
-            oos.writeInt(mComplicationData.mFields.getInt(FIELD_TIMELINE_ENTRY_TYPE));
-
-            List<ComplicationData> listEntries = mComplicationData.getListEntries();
-            int listEntriesLength = (listEntries != null) ? listEntries.size() : 0;
-            oos.writeInt(listEntriesLength);
-            if (listEntries != null) {
-                for (ComplicationData data : listEntries) {
-                    new SerializedForm(data).writeObject(oos);
-                }
-            }
-
-            if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
-                ComplicationData placeholder = mComplicationData.getPlaceholder();
-                if (placeholder == null) {
-                    oos.writeBoolean(false);
-                } else {
-                    oos.writeBoolean(true);
-                    new SerializedForm(placeholder).writeObject(oos);
-                }
-            }
-
-            // This has to be last, since it's recursive.
-            List<ComplicationData> timeline = mComplicationData.getTimelineEntries();
-            int timelineLength = (timeline != null) ? timeline.size() : 0;
-            oos.writeInt(timelineLength);
-            if (timeline != null) {
-                for (ComplicationData data : timeline) {
-                    new SerializedForm(data).writeObject(oos);
-                }
-            }
-        }
-
-        private static void putIfNotNull(Bundle fields, String field, Parcelable value) {
-            if (value != null) {
-                fields.putParcelable(field, value);
-            }
-        }
-
-        @SuppressLint("SyntheticAccessor") // For mComplicationData.mFields
-        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
-            int versionNumber = ois.readInt();
-            if (versionNumber != VERSION_NUMBER) {
-                // Give up if there's a version skew.
-                throw new IOException("Unsupported serialization version number " + versionNumber);
-            }
-            int type = ois.readInt();
-            Bundle fields = new Bundle();
-            fields.putInt(FIELD_PERSISTENCE_POLICY, ois.readInt());
-            fields.putInt(FIELD_DISPLAY_POLICY, ois.readInt());
-
-            if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
-                putIfNotNull(fields, FIELD_LONG_TEXT, (ComplicationText) ois.readObject());
-            }
-            if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
-                putIfNotNull(fields, FIELD_LONG_TITLE, (ComplicationText) ois.readObject());
-            }
-            if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
-                putIfNotNull(fields, FIELD_SHORT_TEXT, (ComplicationText) ois.readObject());
-            }
-            if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
-                putIfNotNull(fields, FIELD_SHORT_TITLE, (ComplicationText) ois.readObject());
-            }
-            if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
-                putIfNotNull(fields, FIELD_CONTENT_DESCRIPTION,
-                        (ComplicationText) ois.readObject());
-            }
-            if (isFieldValidForType(FIELD_ICON, type)) {
-                putIfNotNull(fields, FIELD_ICON, IconSerializableHelper.read(ois));
-            }
-            if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
-                putIfNotNull(fields, FIELD_ICON_BURN_IN_PROTECTION,
-                        IconSerializableHelper.read(ois));
-            }
-            if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
-                putIfNotNull(fields, FIELD_SMALL_IMAGE, IconSerializableHelper.read(ois));
-            }
-            if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
-                putIfNotNull(fields,
-                        FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, IconSerializableHelper.read(ois));
-            }
-            if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
-                fields.putInt(FIELD_IMAGE_STYLE, ois.readInt());
-            }
-            if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
-                fields.putParcelable(FIELD_LARGE_IMAGE, IconSerializableHelper.read(ois));
-            }
-            if (isFieldValidForType(FIELD_VALUE, type)) {
-                fields.putFloat(FIELD_VALUE, ois.readFloat());
-            }
-            if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
-                fields.putInt(FIELD_VALUE_TYPE, ois.readInt());
-            }
-            if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
-                fields.putFloat(FIELD_MIN_VALUE, ois.readFloat());
-            }
-            if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
-                fields.putFloat(FIELD_MAX_VALUE, ois.readFloat());
-            }
-            if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
-                fields.putFloat(FIELD_TARGET_VALUE, ois.readFloat());
-            }
-            if (isFieldValidForType(FIELD_COLOR_RAMP, type) && ois.readBoolean()) {
-                int numColors = ois.readInt();
-                int[] colors = new int[numColors];
-                for (int i = 0; i < numColors; ++i) {
-                    colors[i] = ois.readInt();
-                }
-                fields.putIntArray(FIELD_COLOR_RAMP, colors);
-            }
-            if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type) && ois.readBoolean()) {
-                fields.putBoolean(FIELD_COLOR_RAMP_INTERPOLATED, ois.readBoolean());
-            }
-            if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type) && ois.readBoolean()) {
-                int numWeights = ois.readInt();
-                float[] weights = new float[numWeights];
-                for (int i = 0; i < numWeights; ++i) {
-                    weights[i] = ois.readFloat();
-                }
-                fields.putFloatArray(FIELD_ELEMENT_WEIGHTS, weights);
-            }
-            if (isFieldValidForType(FIELD_ELEMENT_COLORS, type) && ois.readBoolean()) {
-                int numColors = ois.readInt();
-                int[] colors = new int[numColors];
-                for (int i = 0; i < numColors; ++i) {
-                    colors[i] = ois.readInt();
-                }
-                fields.putIntArray(FIELD_ELEMENT_COLORS, colors);
-            }
-            if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
-                fields.putInt(FIELD_ELEMENT_BACKGROUND_COLOR, ois.readInt());
-            }
-            if (isFieldValidForType(FIELD_START_TIME, type)) {
-                fields.putLong(FIELD_START_TIME, ois.readLong());
-            }
-            if (isFieldValidForType(FIELD_END_TIME, type)) {
-                fields.putLong(FIELD_END_TIME, ois.readLong());
-            }
-            int listEntryType = ois.readInt();
-            if (listEntryType != 0) {
-                fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, listEntryType);
-            }
-            if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
-                fields.putInt(EXP_FIELD_LIST_STYLE_HINT, ois.readInt());
-            }
-            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
-                int length = ois.readInt();
-                if (length > 0) {
-                    byte[] protoLayout = new byte[length];
-                    ois.readFully(protoLayout);
-                    fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout);
-                }
-            }
-            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
-                int length = ois.readInt();
-                if (length > 0) {
-                    byte[] ambientProtoLayout = new byte[length];
-                    ois.readFully(ambientProtoLayout);
-                    fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout);
-                }
-            }
-            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
-                int length = ois.readInt();
-                if (length > 0) {
-                    byte[] protoLayoutResources = new byte[length];
-                    ois.readFully(protoLayoutResources);
-                    fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES, protoLayoutResources);
-                }
-            }
-            if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
-                String componentName = ois.readUTF();
-                if (componentName.isEmpty()) {
-                    fields.remove(FIELD_DATA_SOURCE);
-                } else {
-                    fields.putParcelable(
-                            FIELD_DATA_SOURCE, ComponentName.unflattenFromString(componentName));
-                }
-            }
-            if (ois.readBoolean()) {
-                fields.putBoolean(FIELD_TAP_ACTION_LOST, true);
-            }
-            long start = ois.readLong();
-            if (start != -1) {
-                fields.putLong(FIELD_TIMELINE_START_TIME, start);
-            }
-            long end = ois.readLong();
-            if (end != -1) {
-                fields.putLong(FIELD_TIMELINE_END_TIME, end);
-            }
-            int timelineEntryType = ois.readInt();
-            if (timelineEntryType != 0) {
-                fields.putInt(FIELD_TIMELINE_ENTRY_TYPE, timelineEntryType);
-            }
-
-            int listEntriesLength = ois.readInt();
-            if (listEntriesLength != 0) {
-                Parcelable[] parcels = new Parcelable[listEntriesLength];
-                for (int i = 0; i < listEntriesLength; i++) {
-                    SerializedForm entry = new SerializedForm();
-                    entry.readObject(ois);
-                    parcels[i] = entry.mComplicationData.mFields;
-                }
-                fields.putParcelableArray(EXP_FIELD_LIST_ENTRIES, parcels);
-            }
-
-            if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
-                if (ois.readBoolean()) {
-                    SerializedForm serializedPlaceholder = new SerializedForm();
-                    serializedPlaceholder.readObject(ois);
-                    fields.putInt(FIELD_PLACEHOLDER_TYPE,
-                            serializedPlaceholder.mComplicationData.mType);
-                    fields.putBundle(FIELD_PLACEHOLDER_FIELDS,
-                            serializedPlaceholder.mComplicationData.mFields);
-                }
-            }
-
-            int timelineLength = ois.readInt();
-            if (timelineLength != 0) {
-                Parcelable[] parcels = new Parcelable[timelineLength];
-                for (int i = 0; i < timelineLength; i++) {
-                    SerializedForm entry = new SerializedForm();
-                    entry.readObject(ois);
-                    parcels[i] = entry.mComplicationData.mFields;
-                }
-                fields.putParcelableArray(FIELD_TIMELINE_ENTRIES, parcels);
-            }
-            mComplicationData = new ComplicationData(type, fields);
-        }
-
-        Object readResolve() {
-            return mComplicationData;
-        }
-    }
-
-    @RequiresApi(api = Build.VERSION_CODES.P)
-    Object writeReplace() {
-        return new SerializedForm(this);
-    }
-
-    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
-        throw new InvalidObjectException("Use SerializedForm");
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mType);
-        dest.writeBundle(mFields);
-    }
-
-    /**
-     * Returns the type of this complication data.
-     *
-     * <p>Will be one of {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
-     * #TYPE_RANGED_VALUE}, {@link #TYPE_ICON}, {@link #TYPE_SMALL_IMAGE}, {@link
-     * #TYPE_LARGE_IMAGE}, {@link #TYPE_NOT_CONFIGURED}, {@link #TYPE_EMPTY}, {@link
-     * #TYPE_NO_PERMISSION}, or {@link #TYPE_NO_DATA}.
-     */
-    @ComplicationType
-    public int getType() {
-        return mType;
-    }
-
-    /**
-     * Returns true if the complication is active and should be displayed at the given time. If this
-     * returns false, the complication should not be displayed.
-     *
-     * <p>This must be checked for any time for which the complication will be displayed.
-     */
-    public boolean isActiveAt(long dateTimeMillis) {
-        return dateTimeMillis >= mFields.getLong(FIELD_START_TIME, 0)
-                && dateTimeMillis <= mFields.getLong(FIELD_END_TIME, Long.MAX_VALUE);
-    }
-
-    /**
-     * TapAction unfortunately can't be serialized. Returns true if tapAction has been lost due to
-     * serialization (e.g. due to being read from the local cache). The next complication update
-     * from the system would replace this with one with a tapAction.
-     */
-    public boolean getTapActionLostDueToSerialization() {
-        return mFields.getBoolean(FIELD_TAP_ACTION_LOST);
-    }
-
-    /**
-     * For timeline entries. Returns the epoch second at which this timeline entry becomes
-     * valid or `null` if it's not set.
-     */
-    @Nullable
-    public Long getTimelineStartEpochSecond() {
-        long expiresAt = mFields.getLong(FIELD_TIMELINE_START_TIME, -1);
-        if (expiresAt == -1) {
-            return null;
-        } else {
-            return expiresAt;
-        }
-    }
-
-    /**
-     * For timeline entries. Sets the epoch second at which this timeline entry becomes invalid
-     * or clears the field if instant is `null`.
-     */
-    public void setTimelineStartEpochSecond(@Nullable Long epochSecond) {
-        if (epochSecond == null) {
-            mFields.remove(FIELD_TIMELINE_START_TIME);
-        } else {
-            mFields.putLong(FIELD_TIMELINE_START_TIME, epochSecond);
-        }
-    }
-
-    /**
-     * For timeline entries. Returns the epoch second at which this timeline entry becomes invalid
-     * or `null` if it's not set.
-     */
-    @Nullable
-    public Long getTimelineEndEpochSecond() {
-        long expiresAt = mFields.getLong(FIELD_TIMELINE_END_TIME, -1);
-        if (expiresAt == -1) {
-            return null;
-        } else {
-            return expiresAt;
-        }
-    }
-
-    /**
-     * For timeline entries. Sets the epoch second at which this timeline entry becomes invalid,
-     * or clears the field if instant is `null`.
-     */
-    public void setTimelineEndEpochSecond(@Nullable Long epochSecond) {
-        if (epochSecond == null) {
-            mFields.remove(FIELD_TIMELINE_END_TIME);
-        } else {
-            mFields.putLong(FIELD_TIMELINE_END_TIME, epochSecond);
-        }
-    }
-
-    /** Returns the list of {@link ComplicationData} timeline entries. */
-    @Nullable
-    @SuppressWarnings("deprecation")
-    public List<ComplicationData> getTimelineEntries() {
-        Parcelable[] bundles = mFields.getParcelableArray(FIELD_TIMELINE_ENTRIES);
-        if (bundles == null) {
-            return null;
-        }
-        ArrayList<ComplicationData> entries = new ArrayList<>();
-        for (Parcelable parcelable : bundles) {
-            Bundle bundle = (Bundle) parcelable;
-            bundle.setClassLoader(getClass().getClassLoader());
-            // Use the serialized FIELD_TIMELINE_ENTRY_TYPE or the outer type if it's not there.
-            // Usually the timeline entry type will be the same as the outer type, unless an entry
-            // contains NoDataComplicationData.
-            int type = bundle.getInt(FIELD_TIMELINE_ENTRY_TYPE, mType);
-            entries.add(new ComplicationData(type, (Bundle) parcelable));
-        }
-        return entries;
-    }
-
-    /** Sets the list of {@link ComplicationData} timeline entries. */
-    public void setTimelineEntryCollection(@Nullable Collection<ComplicationData> timelineEntries) {
-        if (timelineEntries == null) {
-            mFields.remove(FIELD_TIMELINE_ENTRIES);
-        } else {
-            mFields.putParcelableArray(
-                    FIELD_TIMELINE_ENTRIES,
-                    timelineEntries.stream().map(
-                            e -> {
-                                // This supports timeline entry of NoDataComplicationData.
-                                e.mFields.putInt(FIELD_TIMELINE_ENTRY_TYPE, e.mType);
-                                return e.mFields;
-                            }
-                    ).toArray(Parcelable[]::new));
-        }
-    }
-
-    /** Returns the list of {@link ComplicationData} entries for a ListComplicationData. */
-    @Nullable
-    @SuppressWarnings("deprecation")
-    public List<ComplicationData> getListEntries() {
-        Parcelable[] bundles = mFields.getParcelableArray(EXP_FIELD_LIST_ENTRIES);
-        if (bundles == null) {
-            return null;
-        }
-        ArrayList<ComplicationData> entries = new ArrayList<>();
-        for (Parcelable parcelable : bundles) {
-            Bundle bundle = (Bundle) parcelable;
-            bundle.setClassLoader(getClass().getClassLoader());
-            entries.add(new ComplicationData(bundle.getInt(EXP_FIELD_LIST_ENTRY_TYPE), bundle));
-        }
-        return entries;
-    }
-
-    /**
-     * Sets the {@link ComponentName} of the ComplicationDataSourceService that provided this
-     * ComplicationData.
-     */
-    public void setDataSource(@Nullable ComponentName provider) {
-        mFields.putParcelable(FIELD_DATA_SOURCE, provider);
-    }
-
-    /**
-     * Gets the {@link ComponentName} of the ComplicationDataSourceService that provided this
-     * ComplicationData.
-     */
-    @Nullable
-    @SuppressWarnings("deprecation")  // The safer alternative is not available on Wear OS yet.
-    public ComponentName getDataSource() {
-        return (ComponentName) mFields.getParcelable(FIELD_DATA_SOURCE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a ranged max value. I.e. if
-     * {@link #getRangedValue} can succeed.
-     */
-    public boolean hasRangedValue() {
-        try {
-            return isFieldValidForType(FIELD_VALUE, mType);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>value</i> field for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
-     * Otherwise returns zero.
-     */
-    public float getRangedValue() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE, mType);
-        return mFields.getFloat(FIELD_VALUE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a ranged max type. I.e. if
-     * {@link #getRangedValueType} can succeed.
-     */
-    public boolean hasRangedValueType() {
-        try {
-            return isFieldValidForType(FIELD_VALUE_TYPE, mType);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>value</i> field for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
-     * Otherwise returns zero.
-     */
-    public int getRangedValueType() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE_TYPE, mType);
-        return mFields.getInt(FIELD_VALUE_TYPE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a ranged max value. I.e. if
-     * {@link #getRangedMinValue} can succeed.
-     */
-    public boolean hasRangedMinValue() {
-        try {
-            return isFieldValidForType(FIELD_MIN_VALUE, mType);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>min value</i> field for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
-     * Otherwise returns zero.
-     */
-    public float getRangedMinValue() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_MIN_VALUE, mType);
-        return mFields.getFloat(FIELD_MIN_VALUE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a ranged max value. I.e. if
-     * {@link #getRangedMaxValue} can succeed.
-     */
-    public boolean hasRangedMaxValue() {
-        try {
-            return isFieldValidForType(FIELD_MAX_VALUE, mType);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>max value</i> field for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
-     * Otherwise returns zero.
-     */
-    public float getRangedMaxValue() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_MAX_VALUE, mType);
-        return mFields.getFloat(FIELD_MAX_VALUE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a ranged max value. I.e. if
-     * {@link #getTargetValue} can succeed.
-     */
-    public boolean hasTargetValue() {
-        try {
-            return isFieldValidForType(FIELD_TARGET_VALUE, mType);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>value</i> field for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_GOAL_PROGRESS}.
-     * Otherwise returns zero.
-     */
-    public float getTargetValue() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_TARGET_VALUE, mType);
-        return mFields.getFloat(FIELD_TARGET_VALUE);
-    }
-
-    /**
-     * Returns the colors for the progress bar.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE} or
-     * {@link #TYPE_GOAL_PROGRESS}.
-     */
-    @ColorInt
-    @Nullable
-    public int[] getColorRamp() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP, mType);
-        if (mFields.containsKey(FIELD_COLOR_RAMP)) {
-            return mFields.getIntArray(FIELD_COLOR_RAMP);
-        }
-        return null;
-    }
-
-    /**
-     * Returns either a boolean where: true means the color ramp colors should be smoothly
-     * interpolatded; false means the color ramp should be rendered in equal sized blocks of
-     * solid color; null means this value wasn't set, i.e. the complication is not of type
-     * {@link #TYPE_RANGED_VALUE} or {@link #TYPE_GOAL_PROGRESS}.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE} or
-     * {@link #TYPE_GOAL_PROGRESS}.
-     */
-    @Nullable
-    public Boolean isColorRampInterpolated() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP_INTERPOLATED, mType);
-        if (mFields.containsKey(FIELD_COLOR_RAMP_INTERPOLATED)) {
-            return mFields.getBoolean(FIELD_COLOR_RAMP_INTERPOLATED);
-        }
-        return null;
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a short title. I.e. if {@link #getShortTitle}
-     * can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasShortTitle() {
-        try {
-            return isFieldValidForType(FIELD_SHORT_TITLE, mType)
-                    && (mFields.getParcelable(FIELD_SHORT_TITLE) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>short title</i> field for this complication, or {@code null} if no value was
-     * provided for the field.
-     *
-     * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
-     * can be obtained for a given point in time.
-     *
-     * <p>The length of the text, including any time-dependent values at any valid time, is expected
-     * to not exceed seven characters. When using this text, the watch face should be able to
-     * display any string of up to seven characters (reducing the text size appropriately if the
-     * string is very wide). Although not expected, it is possible that strings of more than seven
-     * characters might be seen, in which case they may be truncated.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_SHORT_TEXT}, {@link
-     * #TYPE_RANGED_VALUE}, or {@link #TYPE_NO_PERMISSION}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public ComplicationText getShortTitle() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TITLE, mType);
-        return getParcelableField(FIELD_SHORT_TITLE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains short text. I.e. if {@link #getShortText} can
-     * succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasShortText() {
-        try {
-            return isFieldValidForType(FIELD_SHORT_TEXT, mType)
-                    && (mFields.getParcelable(FIELD_SHORT_TEXT) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>short text</i> field for this complication, or {@code null} if no value was
-     * provided for the field.
-     *
-     * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
-     * can be obtained for a given point in time.
-     *
-     * <p>The length of the text, including any time-dependent values at any valid time, is expected
-     * to not exceed seven characters. When using this text, the watch face should be able to
-     * display any string of up to seven characters (reducing the text size appropriately if the
-     * string is very wide). Although not expected, it is possible that strings of more than seven
-     * characters might be seen, in which case they may be truncated.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_SHORT_TEXT}, {@link
-     * #TYPE_RANGED_VALUE}, or {@link #TYPE_NO_PERMISSION}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public ComplicationText getShortText() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TEXT, mType);
-        return getParcelableField(FIELD_SHORT_TEXT);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a long title. I.e. if {@link #getLongTitle}
-     * can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasLongTitle() {
-        try {
-            return isFieldValidForType(FIELD_LONG_TITLE, mType)
-                    && (mFields.getParcelable(FIELD_LONG_TITLE) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>long title</i> field for this complication, or {@code null} if no value was
-     * provided for the field.
-     *
-     * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
-     * can be obtained for a given point in time.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_LONG_TEXT}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public ComplicationText getLongTitle() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TITLE, mType);
-        return getParcelableField(FIELD_LONG_TITLE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains long text. I.e. if {@link #getLongText} can
-     * succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasLongText() {
-        try {
-            return isFieldValidForType(FIELD_LONG_TEXT, mType)
-                    && (mFields.getParcelable(FIELD_LONG_TEXT) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>long text</i> field for this complication.
-     *
-     * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
-     * can be obtained for a given point in time.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_LONG_TEXT}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public ComplicationText getLongText() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TEXT, mType);
-        return getParcelableField(FIELD_LONG_TEXT);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains an Icon. I.e. if {@link #getIcon} can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasIcon() {
-        try {
-            return isFieldValidForType(FIELD_ICON, mType)
-                    && (mFields.getParcelable(FIELD_ICON) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>icon</i> field for this complication, or {@code null} if no value was provided
-     * for the field. The image returned is expected to be single-color and so may be tinted to
-     * whatever color the watch face requires (but note that {@link Drawable#mutate()} should be
-     * called before drawables are tinted).
-     *
-     * <p>If the device is in ambient mode, and utilises burn-in protection, then the result of
-     * {@link #getBurnInProtectionIcon} must be used instead of this.
-     *
-     * <p>Valid for the types {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
-     * #TYPE_RANGED_VALUE}, {@link #TYPE_ICON}, or {@link #TYPE_NO_PERMISSION}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public Icon getIcon() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_ICON, mType);
-        return getParcelableField(FIELD_ICON);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a burn in protection Icon. I.e. if
-     * {@link #getBurnInProtectionIcon} can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasBurnInProtectionIcon() {
-        try {
-            return isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, mType)
-                    && (mFields.getParcelable(FIELD_ICON_BURN_IN_PROTECTION) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the burn-in protection version of the <i>icon</i> field for this complication, or
-     * {@code null} if no such icon was provided. The image returned is expected to be an outline
-     * image suitable for use in ambient mode on screens with burn-in protection. The image is also
-     * expected to be single-color and so may be tinted to whatever color the watch face requires
-     * (but note that {@link Drawable#mutate()} should be called before drawables are tinted, and
-     * that the color used should be suitable for ambient mode with burn-in protection).
-     *
-     * <p>If the device is in ambient mode, and utilises burn-in protection, then the result of this
-     * method must be used instead of the result of {@link #getIcon}.
-     *
-     * <p>Valid for the types {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
-     * #TYPE_RANGED_VALUE}, {@link #TYPE_ICON}, or {@link #TYPE_NO_PERMISSION}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public Icon getBurnInProtectionIcon() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_ICON_BURN_IN_PROTECTION, mType);
-        return getParcelableField(FIELD_ICON_BURN_IN_PROTECTION);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a small image. I.e. if {@link #getSmallImage}
-     * can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasSmallImage() {
-        try {
-            return isFieldValidForType(FIELD_SMALL_IMAGE, mType)
-                    && (mFields.getParcelable(FIELD_SMALL_IMAGE) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>small image</i> field for this complication, or {@code null} if no value was
-     * provided for the field.
-     *
-     * <p>This may be either a {@link #IMAGE_STYLE_PHOTO photo style} image, which is expected to
-     * fill the space available, or an {@link #IMAGE_STYLE_ICON icon style} image, which should be
-     * drawn entirely within the space available. Use {@link #getSmallImageStyle} to determine which
-     * of these applies.
-     *
-     * <p>As this may be any image, it is unlikely to be suitable for display in ambient mode when
-     * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
-     * these circumstances.
-     *
-     * <p>Valid for the types {@link #TYPE_LONG_TEXT} and {@link #TYPE_SMALL_IMAGE}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public Icon getSmallImage() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_SMALL_IMAGE, mType);
-        return getParcelableField(FIELD_SMALL_IMAGE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a burn in protection small image. I.e. if
-     * {@link #getBurnInProtectionSmallImage} can succeed.
-     *
-     * @throws IllegalStateException for invalid types
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasBurnInProtectionSmallImage() {
-        try {
-            return isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, mType)
-                    && (mFields.getParcelable(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the burn-in protection version of the <i>small image</i> field for this complication,
-     * or {@code null} if no such icon was provided. The image returned is expected to be an outline
-     * image suitable for use in ambient mode on screens with burn-in protection. The image is also
-     * expected to be single-color and so may be tinted to whatever color the watch face requires
-     * (but note that {@link Drawable#mutate()} should be called before drawables are tinted, and
-     * that the color used should be suitable for ambient mode with burn-in protection).
-     *
-     * <p>If the device is in ambient mode, and utilises burn-in protection, then the result of this
-     * method must be used instead of the result of {@link #getSmallImage()}.
-     *
-     * <p>Valid for the types {@link #TYPE_LONG_TEXT} and {@link #TYPE_SMALL_IMAGE}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public Icon getBurnInProtectionSmallImage() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, mType);
-        return getParcelableField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION);
-    }
-
-    /**
-     * Returns the <i>small image style</i> field for this complication.
-     *
-     * <p>The result of this method should be taken in to account when drawing a small image
-     * complication.
-     *
-     * <p>Valid only for types that contain small images, i.e. {@link #TYPE_SMALL_IMAGE} and {@link
-     * #TYPE_LONG_TEXT}.
-     * Otherwise returns zero.
-     *
-     * @see #IMAGE_STYLE_PHOTO which can be cropped but not recolored.
-     * @see #IMAGE_STYLE_ICON which can be recolored but not cropped.
-     */
-    @ImageStyle
-    public int getSmallImageStyle() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_IMAGE_STYLE, mType);
-        return mFields.getInt(FIELD_IMAGE_STYLE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a large image. I.e. if {@link #getLargeImage}
-     * can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasLargeImage() {
-        try {
-            return isFieldValidForType(FIELD_LARGE_IMAGE, mType)
-                    && (mFields.getParcelable(FIELD_LARGE_IMAGE) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>large image</i> field for this complication. This image is expected to be of a
-     * suitable size to fill the screen of the watch.
-     *
-     * <p>As this may be any image, it is unlikely to be suitable for display in ambient mode when
-     * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
-     * these circumstances.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_LARGE_IMAGE}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public Icon getLargeImage() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_LARGE_IMAGE, mType);
-        return getParcelableField(FIELD_LARGE_IMAGE);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a tap action. I.e. if {@link #getTapAction}
-     * can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasTapAction() {
-        try {
-            return isFieldValidForType(FIELD_TAP_ACTION, mType)
-                    && (mFields.getParcelable(FIELD_TAP_ACTION) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>tap action</i> field for this complication. The result is a {@link
-     * PendingIntent} that should be fired if the complication is tapped on, assuming the
-     * complication is tappable, or {@code null} if no tap action has been specified.
-     *
-     * <p>Valid for all non-empty types.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public PendingIntent getTapAction() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_TAP_ACTION, mType);
-        return getParcelableField(FIELD_TAP_ACTION);
-    }
-
-    /**
-     * Returns true if the ComplicationData contains a content description. I.e. if
-     * {@link #getContentDescription} can succeed.
-     */
-    @SuppressWarnings("deprecation")
-    public boolean hasContentDescription() {
-        try {
-            return isFieldValidForType(FIELD_CONTENT_DESCRIPTION, mType)
-                    && (mFields.getParcelable(FIELD_CONTENT_DESCRIPTION) != null);
-        } catch (BadParcelableException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the <i>content description </i> field for this complication, for screen readers. This
-     * usually describes the image, but may also describe the overall complication.
-     *
-     * <p>Valid for all non-empty types.
-     */
-    @Nullable
-    public ComplicationText getContentDescription() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_CONTENT_DESCRIPTION, mType);
-        return getParcelableField(FIELD_CONTENT_DESCRIPTION);
-    }
-
-    /**
-     * Returns the element weights for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_WEIGHTED_ELEMENTS}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public float[] getElementWeights() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_WEIGHTS, mType);
-        return mFields.getFloatArray(FIELD_ELEMENT_WEIGHTS);
-    }
-
-    /**
-     * Returns the element colors for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_WEIGHTED_ELEMENTS}.
-     * Otherwise returns null.
-     */
-    @Nullable
-    public int[] getElementColors() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_COLORS, mType);
-        return mFields.getIntArray(FIELD_ELEMENT_COLORS);
-    }
-
-    /**
-     * Returns the background color to use between elements for this complication.
-     *
-     * <p>Valid only if the type of this complication data is {@link #TYPE_WEIGHTED_ELEMENTS}.
-     * Otherwise returns 0.
-     */
-    @ColorInt
-    public int getElementBackgroundColor() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_BACKGROUND_COLOR, mType);
-        return mFields.getInt(FIELD_ELEMENT_BACKGROUND_COLOR);
-    }
-
-    /**
-     * Returns the placeholder ComplicationData if there is one or `null`.
-     */
-    @Nullable
-    public ComplicationData getPlaceholder() {
-        checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, mType);
-        checkFieldValidForType(FIELD_PLACEHOLDER_TYPE, mType);
-        if (!mFields.containsKey(FIELD_PLACEHOLDER_FIELDS)
-                || !mFields.containsKey(FIELD_PLACEHOLDER_TYPE)) {
-            return null;
-        }
-        return new ComplicationData(mFields.getInt(FIELD_PLACEHOLDER_TYPE),
-                mFields.getBundle(FIELD_PLACEHOLDER_FIELDS));
-    }
-
-    /** Returns the bytes of the proto layout. */
-    @Nullable
-    public byte[] getInteractiveLayout() {
-        return mFields.getByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE);
-    }
-
-    /**
-     * Returns the list style hint.
-     *
-     * <p>Valid only if the type of this complication data is {@link #EXP_TYPE_LIST}. Otherwise
-     * returns zero.
-     */
-    public int getListStyleHint() {
-        checkFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, mType);
-        return mFields.getInt(EXP_FIELD_LIST_STYLE_HINT);
-    }
-
-    /** Returns the bytes of the ambient proto layout. */
-    @Nullable
-    public byte[] getAmbientLayout() {
-        return mFields.getByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT);
-    }
-
-    /** Returns the bytes of the proto layout resources. */
-    @Nullable
-    public byte[] getLayoutResources() {
-        return mFields.getByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES);
-    }
-
-    /** Return's the complication's [ComplicationCachePolicy]. */
-    @ComplicationPersistencePolicy
-    public int getPersistencePolicy() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_PERSISTENCE_POLICY, mType);
-        return mFields.getInt(
-                FIELD_PERSISTENCE_POLICY, ComplicationPersistencePolicies.CACHING_ALLOWED);
-    }
-
-    /** Return's the complication's [ComplicationDisplayPolicy]. */
-    @ComplicationDisplayPolicy
-    public int getDisplayPolicy() {
-        checkFieldValidForTypeWithoutThrowingException(FIELD_DISPLAY_POLICY, mType);
-        return mFields.getInt(FIELD_DISPLAY_POLICY, ComplicationDisplayPolicies.ALWAYS_DISPLAY);
-    }
-
-    /**
-     * Returns the start time for this complication data (i.e. the first time at which it should
-     * be considered active and displayed), this may be 0. See also {@link #isActiveAt(long)}.
-     */
-    public long getStartDateTimeMillis() {
-        return mFields.getLong(FIELD_START_TIME, 0);
-    }
-
-    /**
-     * Returns the end time for this complication data (i.e. the last time at which it should be
-     * considered active and displayed), this may be {@link Long#MAX_VALUE}. See also {@link
-     * #isActiveAt(long)}.
-     */
-    public long getEndDateTimeMillis() {
-        return mFields.getLong(FIELD_END_TIME, Long.MAX_VALUE);
-    }
-
-    /**
-     * Returns true if the complication data contains at least one text field with a value that may
-     * change based on the current time.
-     */
-    public boolean isTimeDependent() {
-        return isTimeDependentField(FIELD_SHORT_TEXT)
-                || isTimeDependentField(FIELD_SHORT_TITLE)
-                || isTimeDependentField(FIELD_LONG_TEXT)
-                || isTimeDependentField(FIELD_LONG_TITLE);
-    }
-
-    private boolean isTimeDependentField(String field) {
-        ComplicationText text = getParcelableField(field);
-
-        return text != null && text.isTimeDependent();
-    }
-
-    static boolean isFieldValidForType(String field, @ComplicationType int type) {
-        String[] requiredFields = REQUIRED_FIELDS.get(type);
-        if (requiredFields == null) {
-            return false;
-        }
-        for (String requiredField : requiredFields) {
-            if (requiredField.equals(field)) {
-                return true;
-            }
-        }
-        for (String optionalField : Objects.requireNonNull(OPTIONAL_FIELDS.get(type))) {
-            if (optionalField.equals(field)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static boolean isTypeSupported(int type) {
-        return VALID_TYPES.contains(type);
-    }
-
-    /**
-     * The unparceling logic needs to remain backward compatible.
-     */
-    private static void checkFieldValidForTypeWithoutThrowingException(
-            String field, @ComplicationType int type) {
-        if (!isTypeSupported(type)) {
-            Log.w(TAG, "Type " + type + " can not be recognized");
-            return;
-        }
-        if (!isFieldValidForType(field, type)) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Field " + field + " is not supported for type " + type);
-            }
-        }
-    }
-
-    private static void checkFieldValidForType(String field, @ComplicationType int type) {
-        if (!isTypeSupported(type)) {
-            throw new IllegalStateException("Type " + type + " can not be recognized");
-        }
-        if (!isFieldValidForType(field, type)) {
-            throw new IllegalStateException(
-                    "Field " + field + " is not supported for type " + type);
-        }
-    }
-
-    @SuppressWarnings({"TypeParameterUnusedInFormals", "deprecation"})
-    private <T extends Parcelable> T getParcelableField(String field) {
-        try {
-            return mFields.getParcelable(field);
-        } catch (BadParcelableException e) {
-            Log.w(
-                    TAG,
-                    "Could not unparcel ComplicationData. Provider apps must exclude wearable "
-                            + "support complication classes from proguard.",
-                    e);
-            return null;
-        }
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        if (shouldRedact()) {
-            return "ComplicationData{" + "mType=" + mType + ", mFields=REDACTED}";
-        }
-        return toStringNoRedaction();
-    }
-
-    /** @hide */
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public String toStringNoRedaction() {
-        return "ComplicationData{" + "mType=" + mType + ", mFields=" + mFields + '}';
-    }
-
-    /** Builder class for {@link ComplicationData}. */
-    public static final class Builder {
-        @ComplicationType
-        final int mType;
-        final Bundle mFields;
-
-        /** Creates a builder from given {@link ComplicationData}, copying its type and data. */
-        @SuppressLint("SyntheticAccessor")
-        public Builder(@NonNull ComplicationData data) {
-            mType = data.getType();
-            mFields = (Bundle) data.mFields.clone();
-        }
-
-        public Builder(@ComplicationType int type) {
-            mType = type;
-            mFields = new Bundle();
-            if (type == TYPE_SMALL_IMAGE || type == TYPE_LONG_TEXT) {
-                setSmallImageStyle(IMAGE_STYLE_PHOTO);
-            }
-        }
-
-        /** Sets the complication's [ComplicationCachePolicy]. */
-        @NonNull
-        public Builder setPersistencePolicy(@ComplicationPersistencePolicy int cachePolicy) {
-            mFields.putInt(FIELD_PERSISTENCE_POLICY, cachePolicy);
-            return this;
-        }
-
-        /** Sets the complication's [ComplicationDisplayPolicy]. */
-        @NonNull
-        public Builder setDisplayPolicy(@ComplicationDisplayPolicy int displayPolicy) {
-            mFields.putInt(FIELD_DISPLAY_POLICY, displayPolicy);
-            return this;
-        }
-
-        /**
-         * Sets the start time for this complication data. This is optional for any type.
-         *
-         * <p>The complication data will be considered inactive (i.e. should not be displayed) if
-         * the current time is less than the start time. If not specified, the data is considered
-         * active for all time up to the end time (or always active if end time is also not
-         * specified).
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setStartDateTimeMillis(long startDateTimeMillis) {
-            mFields.putLong(FIELD_START_TIME, startDateTimeMillis);
-            return this;
-        }
-
-        /**
-         * Removes the start time for this complication data.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder clearStartDateTime() {
-            mFields.remove(FIELD_START_TIME);
-            return this;
-        }
-
-        /**
-         * Sets the end time for this complication data. This is optional for any type.
-         *
-         * <p>The complication data will be considered inactive (i.e. should not be displayed) if
-         * the current time is greater than the end time. If not specified, the data is considered
-         * active for all time after the start time (or always active if start time is also not
-         * specified).
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setEndDateTimeMillis(long endDateTimeMillis) {
-            mFields.putLong(FIELD_END_TIME, endDateTimeMillis);
-            return this;
-        }
-
-        /**
-         * Removes the end time for this complication data.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder clearEndDateTime() {
-            mFields.remove(FIELD_END_TIME);
-            return this;
-        }
-
-        /**
-         * Sets the <i>value</i> field. This is required for the {@link #TYPE_RANGED_VALUE} type,
-         * and the {@link #TYPE_GOAL_PROGRESS} type. For {@link #TYPE_RANGED_VALUE} value must
-         * be in the range [min .. max] for {@link #TYPE_GOAL_PROGRESS} value must be >= and may
-         * be greater than target value.
-         *
-         * <p>Both the {@link #TYPE_RANGED_VALUE} complication and the
-         * {@link #TYPE_GOAL_PROGRESS} complication visually present a single value, which is
-         * usually a percentage. E.g. you have completed 70% of today's  target of 10000 steps.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setRangedValue(float value) {
-            putFloatField(FIELD_VALUE, value);
-            return this;
-        }
-
-        /**
-         * Sets the <i>value type</i> field which provides meta data about the value. This is
-         * optional for the {@link #TYPE_RANGED_VALUE} type.
-         */
-        @NonNull
-        public Builder setRangedValueType(int valueType) {
-            putIntField(FIELD_VALUE_TYPE, valueType);
-            return this;
-        }
-
-        /**
-         * Sets the <i>min value</i> field. This is required for the {@link #TYPE_RANGED_VALUE}
-         * type, and is not valid for any other type. A {@link #TYPE_RANGED_VALUE} complication
-         * visually presents a single value, which is usually a percentage. E.g. you have
-         * completed 70% of today's target of 10000 steps.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setRangedMinValue(float minValue) {
-            putFloatField(FIELD_MIN_VALUE, minValue);
-            return this;
-        }
-
-        /**
-         * Sets the <i>max value</i> field. This is required for the {@link #TYPE_RANGED_VALUE}
-         * type, and is not valid for any other type.A {@link #TYPE_RANGED_VALUE} complication
-         * visually presents a single value, which is usually a percentage. E.g. you have
-         * completed 70% of today's target of 10000 steps.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setRangedMaxValue(float maxValue) {
-            putFloatField(FIELD_MAX_VALUE, maxValue);
-            return this;
-        }
-
-        /**
-         * Sets the <i>targetValue</i> field. This is required for the
-         * {@link #TYPE_GOAL_PROGRESS} type, and is not valid for any other type. A
-         * {@link #TYPE_GOAL_PROGRESS} complication visually presents a single value, which
-         * is usually a percentage. E.g. you have completed 70% of today's target of 10000 steps.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setTargetValue(float targetValue) {
-            putFloatField(FIELD_TARGET_VALUE, targetValue);
-            return this;
-        }
-
-        /**
-         * Sets the <i>long title</i> field. This is optional for the {@link #TYPE_LONG_TEXT} type,
-         * and is not valid for any other type.
-         *
-         * <p>The value must be provided as a {@link ComplicationText} object, so that
-         * time-dependent values may be included.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setLongTitle(@Nullable ComplicationText longTitle) {
-            putOrRemoveField(FIELD_LONG_TITLE, longTitle);
-            return this;
-        }
-
-        /**
-         * Sets the <i>long text</i> field. This is required for the {@link #TYPE_LONG_TEXT} type,
-         * and is not valid for any other type.
-         *
-         * <p>The value must be provided as a {@link ComplicationText} object, so that
-         * time-dependent values may be included.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setLongText(@Nullable ComplicationText longText) {
-            putOrRemoveField(FIELD_LONG_TEXT, longText);
-            return this;
-        }
-
-        /**
-         * Sets the <i>short title</i> field. This is valid for the {@link #TYPE_SHORT_TEXT}, {@link
-         * #TYPE_RANGED_VALUE}, and {@link #TYPE_NO_PERMISSION} types, and is not valid for any
-         * other type.
-         *
-         * <p>The value must be provided as a {@link ComplicationText} object, so that
-         * time-dependent values may be included.
-         *
-         * <p>The length of the text, including any time-dependent values, should not exceed seven
-         * characters. If it does, the text may be truncated by the watch face or might not fit in
-         * the complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setShortTitle(@Nullable ComplicationText shortTitle) {
-            putOrRemoveField(FIELD_SHORT_TITLE, shortTitle);
-            return this;
-        }
-
-        /**
-         * Sets the <i>short text</i> field. This is required for the {@link #TYPE_SHORT_TEXT} type,
-         * is optional for the {@link #TYPE_RANGED_VALUE} and {@link #TYPE_NO_PERMISSION} types, and
-         * is not valid for any other type.
-         *
-         * <p>The value must be provided as a {@link ComplicationText} object, so that
-         * time-dependent values may be included.
-         *
-         * <p>The length of the text, including any time-dependent values, should not exceed seven
-         * characters. If it does, the text may be truncated by the watch face or might not fit in
-         * the complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setShortText(@Nullable ComplicationText shortText) {
-            putOrRemoveField(FIELD_SHORT_TEXT, shortText);
-            return this;
-        }
-
-        /**
-         * Sets the <i>icon</i> field. This is required for the {@link #TYPE_ICON} type, and is
-         * optional for the {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
-         * #TYPE_RANGED_VALUE}, and {@link #TYPE_NO_PERMISSION} types.
-         *
-         * <p>The provided image must be single-color, so that watch faces can tint it as required.
-         *
-         * <p>If the icon provided here is not suitable for display in ambient mode with burn-in
-         * protection (e.g. if it includes solid blocks of pixels), then a burn-in safe version of
-         * the icon must be provided via {@link #setBurnInProtectionIcon}.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setIcon(@Nullable Icon icon) {
-            putOrRemoveField(FIELD_ICON, icon);
-            return this;
-        }
-
-        /**
-         * Sets the burn-in protection version of the <i>icon</i> field. This should be provided if
-         * the <i>icon</i> field is provided, unless the main icon is already safe for use with
-         * burn-in protection.  This icon should have fewer lit pixels, and should use darker
-         * colors to prevent LCD burn in issues.
-         *
-         * <p>The provided image must be single-color, so that watch faces can tint it as required.
-         *
-         * <p>The provided image must not contain solid blocks of pixels - it should instead be
-         * composed of outlines or lines only.
-         *
-         * <p>If this field is set, the <i>icon</i> field must also be set.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setBurnInProtectionIcon(@Nullable Icon icon) {
-            putOrRemoveField(FIELD_ICON_BURN_IN_PROTECTION, icon);
-            return this;
-        }
-
-        /**
-         * Sets the <i>small image</i> field. This is required for the {@link #TYPE_SMALL_IMAGE}
-         * type, and is optional for the {@link #TYPE_LONG_TEXT} type.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setSmallImage(@Nullable Icon smallImage) {
-            putOrRemoveField(FIELD_SMALL_IMAGE, smallImage);
-            return this;
-        }
-
-        /**
-         * Sets the burn-in protection version of the <i>small image</i> field. This should be
-         * provided if the <i>small image</i> field is provided, unless the main small image is
-         * already safe for use with burn-in protection.
-         *
-         * <p>The provided image must not contain solid blocks of pixels - it should instead be
-         * composed of outlines or lines only.
-         *
-         * <p>If this field is set, the <i>small image</i> field must also be set.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setBurnInProtectionSmallImage(@Nullable Icon smallImage) {
-            putOrRemoveField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, smallImage);
-            return this;
-        }
-
-        /**
-         * Sets the display style for this complication data. This is valid only for types that
-         * contain small images, i.e. {@link #TYPE_SMALL_IMAGE} and {@link #TYPE_LONG_TEXT}.
-         *
-         * <p>This affects how watch faces will draw the image in the complication.
-         *
-         * <p>If not specified, the default is {@link #IMAGE_STYLE_PHOTO}.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         * @see #IMAGE_STYLE_PHOTO which can be cropped but not recolored.
-         * @see #IMAGE_STYLE_ICON which can be recolored but not cropped.
-         */
-        @NonNull
-        public Builder setSmallImageStyle(@ImageStyle int imageStyle) {
-            putIntField(FIELD_IMAGE_STYLE, imageStyle);
-            return this;
-        }
-
-        /**
-         * Sets the <i>large image</i> field. This is required for the {@link #TYPE_LARGE_IMAGE}
-         * type, and is not valid for any other type.
-         *
-         * <p>The provided image should be suitably sized to fill the screen of the watch.
-         *
-         * <p>Returns this Builder to allow chaining.
-         *
-         * @throws IllegalStateException if this field is not valid for the complication type
-         */
-        @NonNull
-        public Builder setLargeImage(@Nullable Icon largeImage) {
-            putOrRemoveField(FIELD_LARGE_IMAGE, largeImage);
-            return this;
-        }
-
-        /**
-         * Sets the list style hint
-         *
-         * <p>Valid only if the type of this complication data is {@link #EXP_TYPE_LIST}. Otherwise
-         * returns
-         * zero.
-         */
-        @NonNull
-        public Builder setListStyleHint(int listStyleHint) {
-            putIntField(EXP_FIELD_LIST_STYLE_HINT, listStyleHint);
-            return this;
-        }
-
-        /**
-         * Sets the <i>tap action</i> field. This is optional for any non-empty type.
-         *
-         * <p>The provided {@link PendingIntent} may be fired if the complication is tapped on. Note
-         * that some complications might not be tappable, in which case this field will be ignored.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setTapAction(@Nullable PendingIntent pendingIntent) {
-            putOrRemoveField(FIELD_TAP_ACTION, pendingIntent);
-            return this;
-        }
-
-        /**
-         * Sets the <i>content description</i> field for accessibility. This is optional for any
-         * non-empty type. It is recommended to provide a content description whenever the
-         * data includes an image.
-         *
-         * <p>The provided text will be read aloud by a Text-to-speech converter for users who may
-         * be vision-impaired. It will be read aloud in addition to any long, short, or range text
-         * in the complication.
-         *
-         * <p>If using to describe an image/icon that is purely stylistic and doesn't convey any
-         * information to the user, you may set the image content description to an empty string
-         * ("").
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setContentDescription(@Nullable ComplicationText description) {
-            putOrRemoveField(FIELD_CONTENT_DESCRIPTION, description);
-            return this;
-        }
-
-        /**
-         * Sets whether or not this ComplicationData has been serialized.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setTapActionLostDueToSerialization(boolean tapActionLostDueToSerialization) {
-            if (tapActionLostDueToSerialization) {
-                mFields.putBoolean(FIELD_TAP_ACTION_LOST, tapActionLostDueToSerialization);
-            }
-            return this;
-        }
-
-        /**
-         * Sets the placeholder.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @SuppressLint("SyntheticAccessor")
-        @NonNull
-        public Builder setPlaceholder(@Nullable ComplicationData placeholder) {
-            if (placeholder == null) {
-                mFields.remove(FIELD_PLACEHOLDER_FIELDS);
-                mFields.remove(FIELD_PLACEHOLDER_TYPE);
-            } else {
-                ComplicationData.checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, mType);
-                mFields.putBundle(FIELD_PLACEHOLDER_FIELDS, placeholder.mFields);
-                putIntField(FIELD_PLACEHOLDER_TYPE, placeholder.mType);
-            }
-            return this;
-        }
-
-        /**
-         * Sets the {@link ComponentName} of the ComplicationDataSourceService that provided this
-         * ComplicationData. Generally this field should be set and is only nullable for backwards
-         * compatibility.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setDataSource(@Nullable ComponentName provider) {
-            putOrRemoveField(FIELD_DATA_SOURCE, provider);
-            return this;
-        }
-
-        /**
-         * Sets the ambient proto layout associated with this complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setAmbientLayout(@NonNull byte[] ambientProtoLayout) {
-            putByteArrayField(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout);
-            return this;
-        }
-
-        /**
-         * Sets the proto layout associated with this complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setInteractiveLayout(@NonNull byte[] protoLayout) {
-            putByteArrayField(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout);
-            return this;
-        }
-
-        /**
-         * Sets the proto layout resources associated with this complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setLayoutResources(@NonNull byte[] resources) {
-            putByteArrayField(EXP_FIELD_PROTO_LAYOUT_RESOURCES, resources);
-            return this;
-        }
-
-        /**
-         * Optional. Sets the color the color ramp should be drawn with.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setColorRamp(@Nullable int[] colorRamp) {
-            putOrRemoveField(FIELD_COLOR_RAMP, colorRamp);
-            return this;
-        }
-
-        /**
-         * Optional. Sets whether or not the color ramp should be smootly shaded or drawn with
-         * steps.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setColorRampIsSmoothShaded(@Nullable Boolean isSmoothShaded) {
-            putOrRemoveField(FIELD_COLOR_RAMP_INTERPOLATED, isSmoothShaded);
-            return this;
-        }
-
-        /**
-         * Sets the list of {@link ComplicationData} timeline entries.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setListEntryCollection(
-                @Nullable Collection<ComplicationData> timelineEntries) {
-            if (timelineEntries == null) {
-                mFields.remove(EXP_FIELD_LIST_ENTRIES);
-            } else {
-                mFields.putParcelableArray(
-                        EXP_FIELD_LIST_ENTRIES,
-                        timelineEntries.stream()
-                                .map(
-                                        e -> {
-                                            e.mFields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, e.mType);
-                                            return e.mFields;
-                                        })
-                                .toArray(Parcelable[]::new));
-            }
-            return this;
-        }
-
-        /**
-         * Sets the element weights for this complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setElementWeights(@Nullable float[] elementWeights) {
-            putOrRemoveField(FIELD_ELEMENT_WEIGHTS, elementWeights);
-            return this;
-        }
-
-        /**
-         * Sets the element colors for this complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setElementColors(@Nullable int[] elementColors) {
-            putOrRemoveField(FIELD_ELEMENT_COLORS, elementColors);
-            return this;
-        }
-
-        /**
-         * Sets the background color to use between elements for this complication.
-         *
-         * <p>Returns this Builder to allow chaining.
-         */
-        @NonNull
-        public Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor) {
-            putOrRemoveField(FIELD_ELEMENT_BACKGROUND_COLOR, elementBackgroundColor);
-            return this;
-        }
-
-        /**
-         * Constructs and returns {@link ComplicationData} with the provided fields. All required
-         * fields must be populated before this method is called.
-         *
-         * @throws IllegalStateException if the required fields have not been populated
-         */
-        @NonNull
-        @SuppressLint("SyntheticAccessor")
-        public ComplicationData build() {
-            // Validate.
-            for (String requiredField : Objects.requireNonNull(REQUIRED_FIELDS.get(mType))) {
-                if (!mFields.containsKey(requiredField)) {
-                    throw new IllegalStateException(
-                            "Field " + requiredField + " is required for type " + mType);
-                }
-
-                if (mFields.containsKey(FIELD_ICON_BURN_IN_PROTECTION)
-                        && !mFields.containsKey(FIELD_ICON)) {
-                    throw new IllegalStateException(
-                            "Field ICON must be provided when field ICON_BURN_IN_PROTECTION is"
-                                    + " provided.");
-                }
-
-                if (mFields.containsKey(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION)
-                        && !mFields.containsKey(FIELD_SMALL_IMAGE)) {
-                    throw new IllegalStateException(
-                            "Field SMALL_IMAGE must be provided when field"
-                                    + " SMALL_IMAGE_BURN_IN_PROTECTION is provided.");
-                }
-            }
-
-            return new ComplicationData(this);
-        }
-
-        @SuppressLint("SyntheticAccessor")
-        private void putIntField(@NonNull String field, int value) {
-            ComplicationData.checkFieldValidForType(field, mType);
-            mFields.putInt(field, value);
-        }
-
-        @SuppressLint("SyntheticAccessor")
-        private void putFloatField(@NonNull String field, float value) {
-            ComplicationData.checkFieldValidForType(field, mType);
-            mFields.putFloat(field, value);
-        }
-
-        @SuppressLint("SyntheticAccessor")
-        private void putByteArrayField(@NonNull String field, @NonNull byte[] value) {
-            ComplicationData.checkFieldValidForType(field, mType);
-            mFields.putByteArray(field, value);
-        }
-
-        /** Sets the field with obj or removes it if null. */
-        @SuppressLint("SyntheticAccessor")
-        private void putOrRemoveField(@NonNull String field, @Nullable Object obj) {
-            ComplicationData.checkFieldValidForType(field, mType);
-            if (obj == null) {
-                mFields.remove(field);
-                return;
-            }
-            if (obj instanceof Boolean) {
-                mFields.putBoolean(field, (Boolean) obj);
-            } else if (obj instanceof Integer) {
-                mFields.putInt(field, (Integer) obj);
-            } else if (obj instanceof String) {
-                mFields.putString(field, (String) obj);
-            } else if (obj instanceof Parcelable) {
-                mFields.putParcelable(field, (Parcelable) obj);
-            } else if (obj instanceof int[]) {
-                mFields.putIntArray(field, (int[]) obj);
-            } else if (obj instanceof float[]) {
-                mFields.putFloatArray(field, (float[]) obj);
-            } else {
-                throw new IllegalArgumentException("Unexpected object type: " + obj.getClass());
-            }
-        }
-    }
-
-    /** Returns whether or not we should redact complication data in toString(). */
-    public static boolean shouldRedact() {
-        return !Log.isLoggable(TAG, Log.DEBUG);
-    }
-
-    @NonNull
-    static String maybeRedact(@Nullable CharSequence unredacted) {
-        if (unredacted == null) {
-            return "(null)";
-        }
-        return maybeRedact(unredacted.toString());
-    }
-
-    @NonNull
-    static String maybeRedact(@NonNull String unredacted) {
-        if (!shouldRedact() || unredacted.equals(PLACEHOLDER_STRING)) {
-            return unredacted;
-        }
-        return "REDACTED";
-    }
-}
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
new file mode 100644
index 0000000..02dc741
--- /dev/null
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
@@ -0,0 +1,2235 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.wearable.complications
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.BadParcelableException
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Log
+import androidx.annotation.ColorInt
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicy
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicy
+import java.io.IOException
+import java.io.InvalidObjectException
+import java.io.ObjectInputStream
+import java.io.ObjectOutputStream
+import java.io.Serializable
+
+/**
+ * Container for complication data of all types.
+ *
+ * A [androidx.wear.watchface.complications.ComplicationProviderService] should create
+ * instances of
+ * this class using [ComplicationData.Builder] and send them to the complication system in
+ * response to
+ * [androidx.wear.watchface.complications.ComplicationProviderService.onComplicationRequest].
+ * Depending on the type of complication data, some fields will be required and some will be
+ * optional - see the documentation for each type, and for the builder's set methods, for details.
+ *
+ * A watch face will receive instances of this class as long as providers are configured.
+ *
+ * When rendering the complication data for a given time, the watch face should first call
+ * [isActiveAt] to determine whether the data is valid at that time. See the documentation for each
+ * of the complication types below for details of which fields are expected to be displayed.
+ *
+ * @hide
+ */
+@SuppressLint("BanParcelableUsage")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ComplicationData : Parcelable, Serializable {
+    /** @hide */
+    @IntDef(
+        TYPE_EMPTY,
+        TYPE_NOT_CONFIGURED,
+        TYPE_SHORT_TEXT,
+        TYPE_LONG_TEXT,
+        TYPE_RANGED_VALUE,
+        TYPE_ICON,
+        TYPE_SMALL_IMAGE,
+        TYPE_LARGE_IMAGE,
+        TYPE_NO_PERMISSION,
+        TYPE_NO_DATA,
+        TYPE_GOAL_PROGRESS,
+        TYPE_WEIGHTED_ELEMENTS,
+        EXP_TYPE_PROTO_LAYOUT,
+        EXP_TYPE_LIST
+    )
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    annotation class ComplicationType
+
+    /** @hide */
+    @IntDef(IMAGE_STYLE_PHOTO, IMAGE_STYLE_ICON)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    annotation class ImageStyle
+
+    /** Returns the type of this complication data. */
+    @ComplicationType
+    val type: Int
+
+    private val fields: Bundle
+
+    internal constructor(builder: Builder) {
+        type = builder.type
+        fields = builder.fields
+    }
+
+    internal constructor(type: Int, fields: Bundle) {
+        this.type = type
+        this.fields = fields
+        this.fields.classLoader = javaClass.classLoader
+    }
+
+    internal constructor(input: Parcel) {
+        type = input.readInt()
+        fields = input.readBundle(javaClass.classLoader) ?: run {
+            Log.w(TAG, "ComplicationData parcel input has null bundle.")
+            Bundle()
+        }
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.P)
+    private class SerializedForm
+    @JvmOverloads
+    constructor(
+        private var complicationData: ComplicationData? = null
+    ) : Serializable {
+
+        @Throws(IOException::class)
+        private fun writeObject(oos: ObjectOutputStream) {
+            // Get a copy because it could technically change to null in another thread.
+            val complicationData = this.complicationData!!
+            oos.writeInt(VERSION_NUMBER)
+            val type = complicationData.type
+            oos.writeInt(type)
+            oos.writeInt(complicationData.persistencePolicy)
+            oos.writeInt(complicationData.displayPolicy)
+            if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
+                oos.writeObject(complicationData.longText)
+            }
+            if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
+                oos.writeObject(complicationData.longTitle)
+            }
+            if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
+                oos.writeObject(complicationData.shortText)
+            }
+            if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
+                oos.writeObject(complicationData.shortTitle)
+            }
+            if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
+                oos.writeObject(complicationData.contentDescription)
+            }
+            if (isFieldValidForType(FIELD_ICON, type)) {
+                oos.writeObject(IconSerializableHelper.create(complicationData.icon))
+            }
+            if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
+                oos.writeObject(
+                    IconSerializableHelper.create(complicationData.burnInProtectionIcon)
+                )
+            }
+            if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
+                oos.writeObject(IconSerializableHelper.create(complicationData.smallImage))
+            }
+            if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
+                oos.writeObject(
+                    IconSerializableHelper.create(
+                        complicationData.burnInProtectionSmallImage
+                    )
+                )
+            }
+            if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
+                oos.writeInt(complicationData.smallImageStyle)
+            }
+            if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
+                oos.writeObject(IconSerializableHelper.create(complicationData.largeImage))
+            }
+            if (isFieldValidForType(FIELD_VALUE, type)) {
+                oos.writeFloat(complicationData.rangedValue)
+            }
+            if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
+                oos.writeInt(complicationData.rangedValueType)
+            }
+            if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
+                oos.writeFloat(complicationData.rangedMinValue)
+            }
+            if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
+                oos.writeFloat(complicationData.rangedMaxValue)
+            }
+            if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
+                oos.writeFloat(complicationData.targetValue)
+            }
+            if (isFieldValidForType(FIELD_COLOR_RAMP, type)) {
+                val colors = complicationData.colorRamp
+                if (colors != null) {
+                    oos.writeBoolean(true)
+                    oos.writeInt(colors.size)
+                    for (color in colors) {
+                        oos.writeInt(color)
+                    }
+                } else {
+                    oos.writeBoolean(false)
+                }
+            }
+            if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type)) {
+                val isColorRampSmoothShaded = complicationData.isColorRampInterpolated
+                if (isColorRampSmoothShaded != null) {
+                    oos.writeBoolean(true)
+                    oos.writeBoolean(isColorRampSmoothShaded)
+                } else {
+                    oos.writeBoolean(false)
+                }
+            }
+            if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type)) {
+                val weights = complicationData.elementWeights
+                if (weights != null) {
+                    oos.writeBoolean(true)
+                    oos.writeInt(weights.size)
+                    for (weight in weights) {
+                        oos.writeFloat(weight)
+                    }
+                } else {
+                    oos.writeBoolean(false)
+                }
+            }
+            if (isFieldValidForType(FIELD_ELEMENT_COLORS, type)) {
+                val colors = complicationData.elementColors
+                if (colors != null) {
+                    oos.writeBoolean(true)
+                    oos.writeInt(colors.size)
+                    for (color in colors) {
+                        oos.writeInt(color)
+                    }
+                } else {
+                    oos.writeBoolean(false)
+                }
+            }
+            if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
+                oos.writeInt(complicationData.elementBackgroundColor)
+            }
+            if (isFieldValidForType(FIELD_START_TIME, type)) {
+                oos.writeLong(complicationData.startDateTimeMillis)
+            }
+            if (isFieldValidForType(FIELD_END_TIME, type)) {
+                oos.writeLong(complicationData.endDateTimeMillis)
+            }
+            oos.writeInt(complicationData.fields.getInt(EXP_FIELD_LIST_ENTRY_TYPE))
+            if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
+                oos.writeInt(complicationData.listStyleHint)
+            }
+            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
+                val bytes = complicationData.interactiveLayout
+                if (bytes == null) {
+                    oos.writeInt(0)
+                } else {
+                    oos.writeInt(bytes.size)
+                    oos.write(bytes)
+                }
+            }
+            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
+                val bytes = complicationData.ambientLayout
+                if (bytes == null) {
+                    oos.writeInt(0)
+                } else {
+                    oos.writeInt(bytes.size)
+                    oos.write(bytes)
+                }
+            }
+            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
+                val bytes = complicationData.layoutResources
+                if (bytes == null) {
+                    oos.writeInt(0)
+                } else {
+                    oos.writeInt(bytes.size)
+                    oos.write(bytes)
+                }
+            }
+            if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
+                val componentName = complicationData.dataSource
+                if (componentName == null) {
+                    oos.writeUTF("")
+                } else {
+                    oos.writeUTF(componentName.flattenToString())
+                }
+            }
+
+            // TapAction unfortunately can't be serialized, instead we record if we've lost it.
+            oos.writeBoolean(
+                complicationData.hasTapAction() || complicationData.tapActionLostDueToSerialization
+            )
+            val start = complicationData.fields.getLong(FIELD_TIMELINE_START_TIME, -1)
+            oos.writeLong(start)
+            val end = complicationData.fields.getLong(FIELD_TIMELINE_END_TIME, -1)
+            oos.writeLong(end)
+            oos.writeInt(complicationData.fields.getInt(FIELD_TIMELINE_ENTRY_TYPE))
+            val listEntries = complicationData.listEntries
+            val listEntriesLength = listEntries?.size ?: 0
+            oos.writeInt(listEntriesLength)
+            if (listEntries != null) {
+                for (data in listEntries) {
+                    SerializedForm(data).writeObject(oos)
+                }
+            }
+            if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
+                val placeholder = complicationData.placeholder
+                if (placeholder == null) {
+                    oos.writeBoolean(false)
+                } else {
+                    oos.writeBoolean(true)
+                    SerializedForm(placeholder).writeObject(oos)
+                }
+            }
+
+            // This has to be last, since it's recursive.
+            val timeline = complicationData.timelineEntries
+            val timelineLength = timeline?.size ?: 0
+            oos.writeInt(timelineLength)
+            if (timeline != null) {
+                for (data in timeline) {
+                    SerializedForm(data).writeObject(oos)
+                }
+            }
+        }
+
+        @Throws(IOException::class, ClassNotFoundException::class)
+        private fun readObject(ois: ObjectInputStream) {
+            val versionNumber = ois.readInt()
+            if (versionNumber != VERSION_NUMBER) {
+                // Give up if there's a version skew.
+                throw IOException("Unsupported serialization version number $versionNumber")
+            }
+            val type = ois.readInt()
+            val fields = Bundle()
+            fields.putInt(FIELD_PERSISTENCE_POLICY, ois.readInt())
+            fields.putInt(FIELD_DISPLAY_POLICY, ois.readInt())
+            if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
+                putIfNotNull(fields, FIELD_LONG_TEXT, ois.readObject() as ComplicationText?)
+            }
+            if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
+                putIfNotNull(fields, FIELD_LONG_TITLE, ois.readObject() as ComplicationText?)
+            }
+            if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
+                putIfNotNull(fields, FIELD_SHORT_TEXT, ois.readObject() as ComplicationText?)
+            }
+            if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
+                putIfNotNull(fields, FIELD_SHORT_TITLE, ois.readObject() as ComplicationText?)
+            }
+            if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
+                putIfNotNull(
+                    fields, FIELD_CONTENT_DESCRIPTION,
+                    ois.readObject() as ComplicationText?
+                )
+            }
+            if (isFieldValidForType(FIELD_ICON, type)) {
+                putIfNotNull(fields, FIELD_ICON, IconSerializableHelper.read(ois))
+            }
+            if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
+                putIfNotNull(
+                    fields, FIELD_ICON_BURN_IN_PROTECTION,
+                    IconSerializableHelper.read(ois)
+                )
+            }
+            if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
+                putIfNotNull(fields, FIELD_SMALL_IMAGE, IconSerializableHelper.read(ois))
+            }
+            if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
+                putIfNotNull(
+                    fields,
+                    FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, IconSerializableHelper.read(ois)
+                )
+            }
+            if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
+                fields.putInt(FIELD_IMAGE_STYLE, ois.readInt())
+            }
+            if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
+                fields.putParcelable(FIELD_LARGE_IMAGE, IconSerializableHelper.read(ois))
+            }
+            if (isFieldValidForType(FIELD_VALUE, type)) {
+                fields.putFloat(FIELD_VALUE, ois.readFloat())
+            }
+            if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
+                fields.putInt(FIELD_VALUE_TYPE, ois.readInt())
+            }
+            if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
+                fields.putFloat(FIELD_MIN_VALUE, ois.readFloat())
+            }
+            if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
+                fields.putFloat(FIELD_MAX_VALUE, ois.readFloat())
+            }
+            if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
+                fields.putFloat(FIELD_TARGET_VALUE, ois.readFloat())
+            }
+            if (isFieldValidForType(FIELD_COLOR_RAMP, type) && ois.readBoolean()) {
+                val numColors = ois.readInt()
+                val colors = IntArray(numColors)
+                for (i in 0 until numColors) {
+                    colors[i] = ois.readInt()
+                }
+                fields.putIntArray(FIELD_COLOR_RAMP, colors)
+            }
+            if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type) && ois.readBoolean()) {
+                fields.putBoolean(FIELD_COLOR_RAMP_INTERPOLATED, ois.readBoolean())
+            }
+            if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type) && ois.readBoolean()) {
+                val numWeights = ois.readInt()
+                val weights = FloatArray(numWeights)
+                for (i in 0 until numWeights) {
+                    weights[i] = ois.readFloat()
+                }
+                fields.putFloatArray(FIELD_ELEMENT_WEIGHTS, weights)
+            }
+            if (isFieldValidForType(FIELD_ELEMENT_COLORS, type) && ois.readBoolean()) {
+                val numColors = ois.readInt()
+                val colors = IntArray(numColors)
+                for (i in 0 until numColors) {
+                    colors[i] = ois.readInt()
+                }
+                fields.putIntArray(FIELD_ELEMENT_COLORS, colors)
+            }
+            if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
+                fields.putInt(FIELD_ELEMENT_BACKGROUND_COLOR, ois.readInt())
+            }
+            if (isFieldValidForType(FIELD_START_TIME, type)) {
+                fields.putLong(FIELD_START_TIME, ois.readLong())
+            }
+            if (isFieldValidForType(FIELD_END_TIME, type)) {
+                fields.putLong(FIELD_END_TIME, ois.readLong())
+            }
+            val listEntryType = ois.readInt()
+            if (listEntryType != 0) {
+                fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, listEntryType)
+            }
+            if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
+                fields.putInt(EXP_FIELD_LIST_STYLE_HINT, ois.readInt())
+            }
+            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
+                val length = ois.readInt()
+                if (length > 0) {
+                    val protoLayout = ByteArray(length)
+                    ois.readFully(protoLayout)
+                    fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout)
+                }
+            }
+            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
+                val length = ois.readInt()
+                if (length > 0) {
+                    val ambientProtoLayout = ByteArray(length)
+                    ois.readFully(ambientProtoLayout)
+                    fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout)
+                }
+            }
+            if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
+                val length = ois.readInt()
+                if (length > 0) {
+                    val protoLayoutResources = ByteArray(length)
+                    ois.readFully(protoLayoutResources)
+                    fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES, protoLayoutResources)
+                }
+            }
+            if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
+                val componentName = ois.readUTF()
+                if (componentName.isEmpty()) {
+                    fields.remove(FIELD_DATA_SOURCE)
+                } else {
+                    fields.putParcelable(
+                        FIELD_DATA_SOURCE, ComponentName.unflattenFromString(componentName)
+                    )
+                }
+            }
+            if (ois.readBoolean()) {
+                fields.putBoolean(FIELD_TAP_ACTION_LOST, true)
+            }
+            val start = ois.readLong()
+            if (start != -1L) {
+                fields.putLong(FIELD_TIMELINE_START_TIME, start)
+            }
+            val end = ois.readLong()
+            if (end != -1L) {
+                fields.putLong(FIELD_TIMELINE_END_TIME, end)
+            }
+            val timelineEntryType = ois.readInt()
+            if (timelineEntryType != 0) {
+                fields.putInt(FIELD_TIMELINE_ENTRY_TYPE, timelineEntryType)
+            }
+            val listEntriesLength = ois.readInt()
+            if (listEntriesLength != 0) {
+                val parcels = arrayOfNulls<Parcelable>(listEntriesLength)
+                for (i in 0 until listEntriesLength) {
+                    val entry = SerializedForm()
+                    entry.readObject(ois)
+                    parcels[i] = entry.complicationData!!.fields
+                }
+                fields.putParcelableArray(EXP_FIELD_LIST_ENTRIES, parcels)
+            }
+            if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
+                if (ois.readBoolean()) {
+                    val serializedPlaceholder = SerializedForm()
+                    serializedPlaceholder.readObject(ois)
+                    fields.putInt(
+                        FIELD_PLACEHOLDER_TYPE,
+                        serializedPlaceholder.complicationData!!.type
+                    )
+                    fields.putBundle(
+                        FIELD_PLACEHOLDER_FIELDS,
+                        serializedPlaceholder.complicationData!!.fields
+                    )
+                }
+            }
+            val timelineLength = ois.readInt()
+            if (timelineLength != 0) {
+                val parcels = arrayOfNulls<Parcelable>(timelineLength)
+                for (i in 0 until timelineLength) {
+                    val entry = SerializedForm()
+                    entry.readObject(ois)
+                    parcels[i] = entry.complicationData!!.fields
+                }
+                fields.putParcelableArray(FIELD_TIMELINE_ENTRIES, parcels)
+            }
+            complicationData = ComplicationData(type, fields)
+        }
+
+        fun readResolve(): Any = complicationData!!
+
+        companion object {
+            private const val VERSION_NUMBER = 19
+            internal fun putIfNotNull(fields: Bundle, field: String, value: Parcelable?) {
+                if (value != null) {
+                    fields.putParcelable(field, value)
+                }
+            }
+        }
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.P)
+    fun writeReplace(): Any = SerializedForm(this)
+
+    @Throws(InvalidObjectException::class)
+    private fun readObject(@Suppress("UNUSED_PARAMETER") stream: ObjectInputStream) {
+        throw InvalidObjectException("Use SerializedForm")
+    }
+
+    override fun describeContents() = 0
+
+    override fun writeToParcel(dest: Parcel, flags: Int) {
+        dest.writeInt(type)
+        dest.writeBundle(fields)
+    }
+
+    /**
+     * Returns true if the complication is active and should be displayed at the given time. If this
+     * returns false, the complication should not be displayed.
+     *
+     * This must be checked for any time for which the complication will be displayed.
+     */
+    fun isActiveAt(dateTimeMillis: Long) =
+        (dateTimeMillis >= fields.getLong(FIELD_START_TIME, 0) &&
+            dateTimeMillis <= fields.getLong(FIELD_END_TIME, Long.MAX_VALUE))
+
+    /**
+     * TapAction unfortunately can't be serialized. Returns true if tapAction has been lost due to
+     * serialization (e.g. due to being read from the local cache). The next complication update
+     * from the system would replace this with one with a tapAction.
+     */
+    val tapActionLostDueToSerialization: Boolean
+        get() = fields.getBoolean(FIELD_TAP_ACTION_LOST)
+
+    /**
+     * For timeline entries. The epoch second at which this timeline entry becomes * valid or `null`
+     * if it's not set.
+     */
+    var timelineStartEpochSecond: Long?
+        get() {
+            val expiresAt = fields.getLong(FIELD_TIMELINE_START_TIME, -1)
+            return if (expiresAt == -1L) {
+                null
+            } else {
+                expiresAt
+            }
+        }
+        set(epochSecond) {
+            if (epochSecond == null) {
+                fields.remove(FIELD_TIMELINE_START_TIME)
+            } else {
+                fields.putLong(FIELD_TIMELINE_START_TIME, epochSecond)
+            }
+        }
+
+    /**
+     * For timeline entries. The epoch second at which this timeline entry becomes invalid or `null`
+     * if it's not set.
+     */
+    var timelineEndEpochSecond: Long?
+        get() {
+            val expiresAt = fields.getLong(FIELD_TIMELINE_END_TIME, -1)
+            return if (expiresAt == -1L) {
+                null
+            } else {
+                expiresAt
+            }
+        }
+        set(epochSecond) {
+            if (epochSecond == null) {
+                fields.remove(FIELD_TIMELINE_END_TIME)
+            } else {
+                fields.putLong(FIELD_TIMELINE_END_TIME, epochSecond)
+            }
+        }
+
+    /** The list of [ComplicationData] timeline entries. */
+    val timelineEntries: List<ComplicationData>?
+        @Suppress("DEPRECATION")
+        get() = fields.getParcelableArray(FIELD_TIMELINE_ENTRIES)
+            ?.map { parcelable ->
+                val bundle = parcelable as Bundle
+                bundle.classLoader = javaClass.classLoader
+                // Use the serialized FIELD_TIMELINE_ENTRY_TYPE or the outer type if it's not there.
+                // Usually the timeline entry type will be the same as the outer type, unless an
+                // entry contains NoDataComplicationData.
+                val type = bundle.getInt(FIELD_TIMELINE_ENTRY_TYPE, type)
+                ComplicationData(type, parcelable)
+            }
+
+    /** Sets the list of [ComplicationData] timeline entries. */
+    fun setTimelineEntryCollection(timelineEntries: Collection<ComplicationData>?) {
+        if (timelineEntries == null) {
+            fields.remove(FIELD_TIMELINE_ENTRIES)
+        } else {
+            fields.putParcelableArray(
+                FIELD_TIMELINE_ENTRIES,
+                timelineEntries.map {
+                    // This supports timeline entry of NoDataComplicationData.
+                    it.fields.putInt(FIELD_TIMELINE_ENTRY_TYPE, it.type)
+                    it.fields
+                }.toTypedArray()
+            )
+        }
+    }
+
+    /** The list of [ComplicationData] entries for a ListComplicationData. */
+    val listEntries: List<ComplicationData>?
+        @Suppress("deprecation")
+        get() = fields.getParcelableArray(EXP_FIELD_LIST_ENTRIES)
+            ?.map { parcelable ->
+                val bundle = parcelable as Bundle
+                bundle.classLoader = javaClass.classLoader
+                ComplicationData(bundle.getInt(EXP_FIELD_LIST_ENTRY_TYPE), bundle)
+            }
+
+    /**
+     * The [ComponentName] of the ComplicationDataSourceService that provided this ComplicationData.
+     */
+    var dataSource: ComponentName?
+        // The safer alternative is not available on Wear OS yet.
+        get() = getParcelableField(FIELD_DATA_SOURCE)
+        set(provider) {
+            fields.putParcelable(FIELD_DATA_SOURCE, provider)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a ranged max value. I.e. if [rangedValue] can
+     * succeed.
+     */
+    fun hasRangedValue(): Boolean = isFieldValidForType(FIELD_VALUE, type)
+
+    /**
+     * Returns the *value* field for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+     * zero.
+     */
+    val rangedValue: Float
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE, type)
+            return fields.getFloat(FIELD_VALUE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a ranged max type. I.e. if [rangedValueType]
+     * can succeed.
+     */
+    fun hasRangedValueType(): Boolean = isFieldValidForType(FIELD_VALUE_TYPE, type)
+
+    /**
+     * Returns the *value* field for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+     * zero.
+     */
+    val rangedValueType: Int
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE_TYPE, type)
+            return fields.getInt(FIELD_VALUE_TYPE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a ranged max value. I.e. if [rangedMinValue]
+     * can succeed.
+     */
+    fun hasRangedMinValue(): Boolean = isFieldValidForType(FIELD_MIN_VALUE, type)
+
+    /**
+     * Returns the *min value* field for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+     * zero.
+     */
+    val rangedMinValue: Float
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_MIN_VALUE, type)
+            return fields.getFloat(FIELD_MIN_VALUE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a ranged max value. I.e. if [rangedMaxValue]
+     * can succeed.
+     */
+    fun hasRangedMaxValue(): Boolean = isFieldValidForType(FIELD_MAX_VALUE, type)
+
+    /**
+     * Returns the *max value* field for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+     * zero.
+     */
+    val rangedMaxValue: Float
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_MAX_VALUE, type)
+            return fields.getFloat(FIELD_MAX_VALUE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a ranged max value. I.e. if [targetValue] can
+     * succeed.
+     */
+    fun hasTargetValue(): Boolean = isFieldValidForType(FIELD_TARGET_VALUE, type)
+
+    /**
+     * Returns the *value* field for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_GOAL_PROGRESS], otherwise returns
+     * zero.
+     */
+    val targetValue: Float
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_TARGET_VALUE, type)
+            return fields.getFloat(FIELD_TARGET_VALUE)
+        }
+
+    /**
+     * Returns the colors for the progress bar.
+     *
+     * Valid only if the type of this complication data is [TYPE_RANGED_VALUE] or
+     * [TYPE_GOAL_PROGRESS].
+     */
+    @get:ColorInt
+    val colorRamp: IntArray?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP, type)
+            return if (fields.containsKey(FIELD_COLOR_RAMP)) {
+                fields.getIntArray(FIELD_COLOR_RAMP)
+            } else {
+                null
+            }
+        }
+
+    /**
+     * Returns either a boolean where: true means the color ramp colors should be smoothly
+     * interpolated; false means the color ramp should be rendered in equal sized blocks of
+     * solid color; null means this value wasn't set, i.e. the complication is not of type
+     * [TYPE_RANGED_VALUE] or [TYPE_GOAL_PROGRESS].
+     *
+     * Valid only if the type of this complication data is [TYPE_RANGED_VALUE] or
+     * [TYPE_GOAL_PROGRESS].
+     */
+    val isColorRampInterpolated: Boolean?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP_INTERPOLATED, type)
+            return if (fields.containsKey(FIELD_COLOR_RAMP_INTERPOLATED)) {
+                fields.getBoolean(FIELD_COLOR_RAMP_INTERPOLATED)
+            } else {
+                null
+            }
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a short title. I.e. if [shortTitle]
+     * can succeed.
+     */
+    fun hasShortTitle(): Boolean =
+        isFieldValidForType(FIELD_SHORT_TITLE, type) && hasParcelableField(FIELD_SHORT_TITLE)
+
+    /**
+     * Returns the *short title* field for this complication, or `null` if no value was
+     * provided for the field.
+     *
+     * The value is provided as a [ComplicationText] object, from which the text to display
+     * can be obtained for a given point in time.
+     *
+     * The length of the text, including any time-dependent values at any valid time, is expected
+     * to not exceed seven characters. When using this text, the watch face should be able to
+     * display any string of up to seven characters (reducing the text size appropriately if the
+     * string is very wide). Although not expected, it is possible that strings of more than seven
+     * characters might be seen, in which case they may be truncated.
+     *
+     * Valid only if the type of this complication data is [TYPE_SHORT_TEXT], [TYPE_RANGED_VALUE],
+     * or [TYPE_NO_PERMISSION], otherwise returns null.
+     */
+    val shortTitle: ComplicationText?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TITLE, type)
+            return getParcelableFieldOrWarn<ComplicationText>(FIELD_SHORT_TITLE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains short text. I.e. if [shortText] can succeed.
+     */
+    fun hasShortText(): Boolean =
+        isFieldValidForType(FIELD_SHORT_TEXT, type) && hasParcelableField(FIELD_SHORT_TEXT)
+
+    /**
+     * Returns the *short text* field for this complication, or `null` if no value was
+     * provided for the field.
+     *
+     * The value is provided as a [ComplicationText] object, from which the text to display
+     * can be obtained for a given point in time.
+     *
+     * The length of the text, including any time-dependent values at any valid time, is expected
+     * to not exceed seven characters. When using this text, the watch face should be able to
+     * display any string of up to seven characters (reducing the text size appropriately if the
+     * string is very wide). Although not expected, it is possible that strings of more than seven
+     * characters might be seen, in which case they may be truncated.
+     *
+     * Valid only if the type of this complication data is [TYPE_SHORT_TEXT], [TYPE_RANGED_VALUE],
+     * or [TYPE_NO_PERMISSION], otherwise returns null.
+     */
+    val shortText: ComplicationText?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TEXT, type)
+            return getParcelableFieldOrWarn<ComplicationText>(FIELD_SHORT_TEXT)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a long title. I.e. if [longTitle]
+     * can succeed.
+     */
+    fun hasLongTitle(): Boolean =
+        isFieldValidForType(FIELD_LONG_TITLE, type) && hasParcelableField(FIELD_LONG_TITLE)
+
+    /**
+     * Returns the *long title* field for this complication, or `null` if no value was
+     * provided for the field.
+     *
+     * The value is provided as a [ComplicationText] object, from which the text to display
+     * can be obtained for a given point in time.
+     *
+     * Valid only if the type of this complication data is [TYPE_LONG_TEXT], otherwise returns null.
+     */
+    val longTitle: ComplicationText?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TITLE, type)
+            return getParcelableFieldOrWarn<ComplicationText>(FIELD_LONG_TITLE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains long text. I.e. if [longText] can
+     * succeed.
+     */
+    fun hasLongText(): Boolean =
+        isFieldValidForType(FIELD_LONG_TEXT, type) && hasParcelableField(FIELD_LONG_TEXT)
+
+    /**
+     * Returns the *long text* field for this complication.
+     *
+     * The value is provided as a [ComplicationText] object, from which the text to display
+     * can be obtained for a given point in time.
+     *
+     * Valid only if the type of this complication data is [TYPE_LONG_TEXT], otherwise returns null.
+     */
+    val longText: ComplicationText?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TEXT, type)
+            return getParcelableFieldOrWarn<ComplicationText>(FIELD_LONG_TEXT)
+        }
+
+    /** Returns true if the ComplicationData contains an Icon. I.e. if [icon] can succeed. */
+    fun hasIcon(): Boolean =
+        isFieldValidForType(FIELD_ICON, type) && hasParcelableField(FIELD_ICON)
+
+    /**
+     * Returns the *icon* field for this complication, or `null` if no value was provided
+     * for the field. The image returned is expected to be single-color and so may be tinted to
+     * whatever color the watch face requires (but note that
+     * [android.graphics.drawable.Drawable.mutate] should be called before drawables are tinted).
+     *
+     * If the device is in ambient mode, and utilises burn-in protection, then the result of
+     * [burnInProtectionIcon] must be used instead of this.
+     *
+     * Valid for the types [TYPE_SHORT_TEXT], [TYPE_LONG_TEXT], [TYPE_RANGED_VALUE], [TYPE_ICON], or
+     * [TYPE_NO_PERMISSION], otherwise returns null.
+     */
+    val icon: Icon?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_ICON, type)
+            return getParcelableFieldOrWarn<Icon>(FIELD_ICON)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a burn in protection Icon. I.e. if
+     * [burnInProtectionIcon] can succeed.
+     */
+    fun hasBurnInProtectionIcon(): Boolean =
+        isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type) &&
+            hasParcelableField(FIELD_ICON_BURN_IN_PROTECTION)
+
+    /**
+     * Returns the burn-in protection version of the *icon* field for this complication, or
+     * `null` if no such icon was provided. The image returned is expected to be an outline
+     * image suitable for use in ambient mode on screens with burn-in protection. The image is also
+     * expected to be single-color and so may be tinted to whatever color the watch face requires
+     * (but note that [android.graphics.drawable.Drawable.mutate] should be called before drawables
+     * are tinted, and that the color used should be suitable for ambient mode with burn-in
+     * protection).
+     *
+     * If the device is in ambient mode, and utilises burn-in protection, then the result of this
+     * method must be used instead of the result of [icon].
+     *
+     * Valid for the types [TYPE_SHORT_TEXT], [TYPE_LONG_TEXT], [TYPE_RANGED_VALUE], [TYPE_ICON], or
+     * [TYPE_NO_PERMISSION], otherwise returns null.
+     */
+    val burnInProtectionIcon: Icon?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_ICON_BURN_IN_PROTECTION, type)
+            return getParcelableFieldOrWarn<Icon>(FIELD_ICON_BURN_IN_PROTECTION)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a small image. I.e. if [smallImage]
+     * can succeed.
+     */
+    fun hasSmallImage(): Boolean =
+        isFieldValidForType(FIELD_SMALL_IMAGE, type) && hasParcelableField(FIELD_SMALL_IMAGE)
+
+    /**
+     * Returns the *small image* field for this complication, or `null` if no value was
+     * provided for the field.
+     *
+     * This may be either a [IMAGE_STYLE_PHOTO] image, which is expected to
+     * fill the space available, or an [IMAGE_STYLE_ICON] image, which should be
+     * drawn entirely within the space available. Use [smallImageStyle] to determine which
+     * of these applies.
+     *
+     * As this may be any image, it is unlikely to be suitable for display in ambient mode when
+     * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
+     * these circumstances.
+     *
+     * Valid for the types [TYPE_LONG_TEXT] and [TYPE_SMALL_IMAGE].
+     * Otherwise returns null.
+     */
+    val smallImage: Icon?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_SMALL_IMAGE, type)
+            return getParcelableFieldOrWarn<Icon>(FIELD_SMALL_IMAGE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a burn in protection small image. I.e. if
+     * [burnInProtectionSmallImage] can succeed.
+     *
+     * @throws IllegalStateException for invalid types
+     */
+    fun hasBurnInProtectionSmallImage(): Boolean =
+        isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type) &&
+            hasParcelableField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION)
+
+    /**
+     * Returns the burn-in protection version of the *small image* field for this complication,
+     * or `null` if no such icon was provided. The image returned is expected to be an outline
+     * image suitable for use in ambient mode on screens with burn-in protection. The image is also
+     * expected to be single-color and so may be tinted to whatever color the watch face requires
+     * (but note that [android.graphics.drawable.Drawable.mutate] should be called before drawables
+     * are tinted, and that the color used should be suitable for ambient mode with burn-in
+     * protection).
+     *
+     * If the device is in ambient mode, and utilises burn-in protection, then the result of this
+     * method must be used instead of the result of [smallImage].
+     *
+     * Valid for the types [TYPE_LONG_TEXT] and [TYPE_SMALL_IMAGE].
+     * Otherwise returns null.
+     */
+    val burnInProtectionSmallImage: Icon?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                type
+            )
+            return getParcelableFieldOrWarn<Icon>(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION)
+        }
+
+    /**
+     * Returns the *small image style* field for this complication.
+     *
+     * The result of this method should be taken in to account when drawing a small image
+     * complication.
+     *
+     * Valid only for types that contain small images, i.e. [TYPE_SMALL_IMAGE] and [TYPE_LONG_TEXT].
+     * Otherwise returns zero.
+     *
+     * @see IMAGE_STYLE_PHOTO which can be cropped but not recolored.
+     * @see IMAGE_STYLE_ICON which can be recolored but not cropped.
+     */
+    @ImageStyle
+    val smallImageStyle: Int
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_IMAGE_STYLE, type)
+            return fields.getInt(FIELD_IMAGE_STYLE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a large image. I.e. if [largeImage]
+     * can succeed.
+     */
+    fun hasLargeImage(): Boolean =
+        isFieldValidForType(FIELD_LARGE_IMAGE, type) && hasParcelableField(FIELD_LARGE_IMAGE)
+
+    /**
+     * Returns the *large image* field for this complication. This image is expected to be of a
+     * suitable size to fill the screen of the watch.
+     *
+     * As this may be any image, it is unlikely to be suitable for display in ambient mode when
+     * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
+     * these circumstances.
+     *
+     * Valid only if the type of this complication data is [TYPE_LARGE_IMAGE].
+     * Otherwise returns null.
+     */
+    val largeImage: Icon?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_LARGE_IMAGE, type)
+            return getParcelableFieldOrWarn<Icon>(FIELD_LARGE_IMAGE)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a tap action. I.e. if [tapAction]
+     * can succeed.
+     */
+    fun hasTapAction(): Boolean =
+        isFieldValidForType(FIELD_TAP_ACTION, type) && hasParcelableField(FIELD_TAP_ACTION)
+
+    /**
+     * Returns the *tap action* field for this complication. The result is a [PendingIntent] that
+     * should be fired if the complication is tapped on, assuming the complication is tappable, or
+     * `null` if no tap action has been specified.
+     *
+     * Valid for all non-empty types, otherwise returns null.
+     */
+    val tapAction: PendingIntent?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_TAP_ACTION, type)
+            return getParcelableFieldOrWarn<PendingIntent>(FIELD_TAP_ACTION)
+        }
+
+    /**
+     * Returns true if the ComplicationData contains a content description. I.e. if
+     * [contentDescription] can succeed.
+     */
+    fun hasContentDescription(): Boolean =
+        isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type) &&
+            hasParcelableField(FIELD_CONTENT_DESCRIPTION)
+
+    /**
+     * Returns the *content description * field for this complication, for screen readers. This
+     * usually describes the image, but may also describe the overall complication.
+     *
+     * Valid for all non-empty types.
+     */
+    val contentDescription: ComplicationText?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_CONTENT_DESCRIPTION, type)
+            return getParcelableFieldOrWarn<ComplicationText>(FIELD_CONTENT_DESCRIPTION)
+        }
+
+    /**
+     * Returns the element weights for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_WEIGHTED_ELEMENTS].
+     * Otherwise returns null.
+     */
+    val elementWeights: FloatArray?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_WEIGHTS, type)
+            return fields.getFloatArray(FIELD_ELEMENT_WEIGHTS)
+        }
+
+    /**
+     * Returns the element colors for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_WEIGHTED_ELEMENTS].
+     * Otherwise returns null.
+     */
+    val elementColors: IntArray?
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_COLORS, type)
+            return fields.getIntArray(FIELD_ELEMENT_COLORS)
+        }
+
+    /**
+     * Returns the background color to use between elements for this complication.
+     *
+     * Valid only if the type of this complication data is [TYPE_WEIGHTED_ELEMENTS].
+     * Otherwise returns 0.
+     */
+    @get:ColorInt
+    val elementBackgroundColor: Int
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_BACKGROUND_COLOR, type)
+            return fields.getInt(FIELD_ELEMENT_BACKGROUND_COLOR)
+        }
+
+    /** Returns the placeholder ComplicationData if there is one or `null`. */
+    val placeholder: ComplicationData?
+        get() {
+            checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)
+            checkFieldValidForType(FIELD_PLACEHOLDER_TYPE, type)
+            return if (
+                !fields.containsKey(FIELD_PLACEHOLDER_FIELDS) ||
+                !fields.containsKey(FIELD_PLACEHOLDER_TYPE)
+            ) {
+                null
+            } else ComplicationData(
+                fields.getInt(FIELD_PLACEHOLDER_TYPE),
+                fields.getBundle(FIELD_PLACEHOLDER_FIELDS)!!
+            )
+        }
+
+    /** Returns the bytes of the proto layout. */
+    val interactiveLayout: ByteArray?
+        get() = fields.getByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE)
+
+    /**
+     * Returns the list style hint.
+     *
+     * Valid only if the type of this complication data is [EXP_TYPE_LIST]. Otherwise
+     * returns zero.
+     */
+    val listStyleHint: Int
+        get() {
+            checkFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)
+            return fields.getInt(EXP_FIELD_LIST_STYLE_HINT)
+        }
+
+    /** Returns the bytes of the ambient proto layout. */
+    val ambientLayout: ByteArray?
+        get() = fields.getByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT)
+
+    /** Returns the bytes of the proto layout resources. */
+    val layoutResources: ByteArray?
+        get() = fields.getByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES)
+
+    /** Return's the complication's [ComplicationPersistencePolicies]. */
+    @ComplicationPersistencePolicy
+    val persistencePolicy: Int
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_PERSISTENCE_POLICY, type)
+            return fields.getInt(
+                FIELD_PERSISTENCE_POLICY, ComplicationPersistencePolicies.CACHING_ALLOWED
+            )
+        }
+
+    /** Return's the complication's [ComplicationDisplayPolicy]. */
+    @ComplicationDisplayPolicy
+    val displayPolicy: Int
+        get() {
+            checkFieldValidForTypeWithoutThrowingException(FIELD_DISPLAY_POLICY, type)
+            return fields.getInt(FIELD_DISPLAY_POLICY, ComplicationDisplayPolicies.ALWAYS_DISPLAY)
+        }
+
+    /**
+     * Returns the start time for this complication data (i.e. the first time at which it should
+     * be considered active and displayed), this may be 0. See also [isActiveAt].
+     */
+    val startDateTimeMillis: Long
+        get() = fields.getLong(FIELD_START_TIME, 0)
+
+    /**
+     * Returns the end time for this complication data (i.e. the last time at which it should be
+     * considered active and displayed), this may be [Long.MAX_VALUE]. See also [isActiveAt].
+     */
+    val endDateTimeMillis: Long
+        get() = fields.getLong(FIELD_END_TIME, Long.MAX_VALUE)
+
+    /**
+     * Returns true if the complication data contains at least one text field with a value that may
+     * change based on the current time.
+     */
+    val isTimeDependent: Boolean
+        get() = isTimeDependentField(FIELD_SHORT_TEXT) ||
+            isTimeDependentField(FIELD_SHORT_TITLE) ||
+            isTimeDependentField(FIELD_LONG_TEXT) ||
+            isTimeDependentField(FIELD_LONG_TITLE)
+
+    private fun isTimeDependentField(field: String): Boolean {
+        val text = getParcelableFieldOrWarn<ComplicationText>(field)
+        return text != null && text.isTimeDependent
+    }
+
+    private fun <T : Parcelable?> getParcelableField(field: String): T? =
+        try {
+            @Suppress("deprecation")
+            fields.getParcelable<T>(field)
+        } catch (e: BadParcelableException) {
+            null
+        }
+
+    private fun hasParcelableField(field: String) = getParcelableField<Parcelable>(field) != null
+
+    private fun <T : Parcelable?> getParcelableFieldOrWarn(field: String): T? =
+        try {
+            @Suppress("deprecation")
+            fields.getParcelable<T>(field)
+        } catch (e: BadParcelableException) {
+            Log.w(
+                TAG,
+                "Could not unparcel ComplicationData. Provider apps must exclude wearable " +
+                    "support complication classes from proguard.",
+                e
+            )
+            null
+        }
+
+    override fun toString() =
+        if (shouldRedact()) {
+            "ComplicationData{mType=$type, mFields=REDACTED}"
+        } else {
+            toStringNoRedaction()
+        }
+
+    /** @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun toStringNoRedaction() = "ComplicationData{mType=$type, mFields=$fields}"
+
+    /** Builder class for [ComplicationData]. */
+    class Builder {
+        @ComplicationType
+        internal val type: Int
+
+        internal val fields: Bundle
+
+        /** Creates a builder from given [ComplicationData], copying its type and data. */
+        constructor(data: ComplicationData) {
+            type = data.type
+            fields = data.fields.clone() as Bundle
+        }
+
+        constructor(@ComplicationType type: Int) {
+            this.type = type
+            fields = Bundle()
+            if (type == TYPE_SMALL_IMAGE || type == TYPE_LONG_TEXT) {
+                setSmallImageStyle(IMAGE_STYLE_PHOTO)
+            }
+        }
+
+        /** Sets the complication's [ComplicationPersistencePolicy]. */
+        fun setPersistencePolicy(@ComplicationPersistencePolicy cachePolicy: Int) =
+            apply { fields.putInt(FIELD_PERSISTENCE_POLICY, cachePolicy) }
+
+        /** Sets the complication's [ComplicationDisplayPolicy]. */
+        fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int) =
+            apply { fields.putInt(FIELD_DISPLAY_POLICY, displayPolicy) }
+
+        /**
+         * Sets the start time for this complication data. This is optional for any type.
+         *
+         * The complication data will be considered inactive (i.e. should not be displayed) if
+         * the current time is less than the start time. If not specified, the data is considered
+         * active for all time up to the end time (or always active if end time is also not
+         * specified).
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setStartDateTimeMillis(startDateTimeMillis: Long) =
+            apply { fields.putLong(FIELD_START_TIME, startDateTimeMillis) }
+
+        /**
+         * Removes the start time for this complication data.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun clearStartDateTime() = apply { fields.remove(FIELD_START_TIME) }
+
+        /**
+         * Sets the end time for this complication data. This is optional for any type.
+         *
+         * The complication data will be considered inactive (i.e. should not be displayed) if
+         * the current time is greater than the end time. If not specified, the data is considered
+         * active for all time after the start time (or always active if start time is also not
+         * specified).
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setEndDateTimeMillis(endDateTimeMillis: Long) =
+            apply { fields.putLong(FIELD_END_TIME, endDateTimeMillis) }
+
+        /**
+         * Removes the end time for this complication data.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun clearEndDateTime() = apply { fields.remove(FIELD_END_TIME) }
+
+        /**
+         * Sets the *value* field. This is required for the [TYPE_RANGED_VALUE] type,
+         * and the [TYPE_GOAL_PROGRESS] type. For [TYPE_RANGED_VALUE] value must
+         * be in the range [min .. max] for [TYPE_GOAL_PROGRESS] value must be >= and may
+         * be greater than target value.
+         *
+         * Both the [TYPE_RANGED_VALUE] complication and the
+         * [TYPE_GOAL_PROGRESS] complication visually present a single value, which is
+         * usually a percentage. E.g. you have completed 70% of today's  target of 10000 steps.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setRangedValue(value: Float) = apply { putFloatField(FIELD_VALUE, value) }
+
+        /**
+         * Sets the *value type* field which provides meta data about the value. This is
+         * optional for the [TYPE_RANGED_VALUE] type.
+         */
+        fun setRangedValueType(valueType: Int) = apply { putIntField(FIELD_VALUE_TYPE, valueType) }
+
+        /**
+         * Sets the *min value* field. This is required for the [TYPE_RANGED_VALUE]
+         * type, and is not valid for any other type. A [TYPE_RANGED_VALUE] complication
+         * visually presents a single value, which is usually a percentage. E.g. you have
+         * completed 70% of today's target of 10000 steps.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setRangedMinValue(minValue: Float) = apply { putFloatField(FIELD_MIN_VALUE, minValue) }
+
+        /**
+         * Sets the *max value* field. This is required for the [TYPE_RANGED_VALUE]
+         * type, and is not valid for any other type. A [TYPE_RANGED_VALUE] complication
+         * visually presents a single value, which is usually a percentage. E.g. you have
+         * completed 70% of today's target of 10000 steps.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setRangedMaxValue(maxValue: Float) = apply { putFloatField(FIELD_MAX_VALUE, maxValue) }
+
+        /**
+         * Sets the *targetValue* field. This is required for the
+         * [TYPE_GOAL_PROGRESS] type, and is not valid for any other type. A
+         * [TYPE_GOAL_PROGRESS] complication visually presents a single value, which
+         * is usually a percentage. E.g. you have completed 70% of today's target of 10000 steps.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setTargetValue(targetValue: Float) =
+            apply { putFloatField(FIELD_TARGET_VALUE, targetValue) }
+
+        /**
+         * Sets the *long title* field. This is optional for the [TYPE_LONG_TEXT] type,
+         * and is not valid for any other type.
+         *
+         * The value must be provided as a [ComplicationText] object, so that
+         * time-dependent values may be included.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setLongTitle(longTitle: ComplicationText?) =
+            apply { putOrRemoveField(FIELD_LONG_TITLE, longTitle) }
+
+        /**
+         * Sets the *long text* field. This is required for the [TYPE_LONG_TEXT] type,
+         * and is not valid for any other type.
+         *
+         * The value must be provided as a [ComplicationText] object, so that
+         * time-dependent values may be included.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setLongText(longText: ComplicationText?) =
+            apply { putOrRemoveField(FIELD_LONG_TEXT, longText) }
+
+        /**
+         * Sets the *short title* field. This is valid for the [TYPE_SHORT_TEXT],
+         * [TYPE_RANGED_VALUE], and [TYPE_NO_PERMISSION] types, and is not valid for any other type.
+         *
+         * The value must be provided as a [ComplicationText] object, so that
+         * time-dependent values may be included.
+         *
+         * The length of the text, including any time-dependent values, should not exceed seven
+         * characters. If it does, the text may be truncated by the watch face or might not fit in
+         * the complication.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setShortTitle(shortTitle: ComplicationText?) =
+            apply { putOrRemoveField(FIELD_SHORT_TITLE, shortTitle) }
+
+        /**
+         * Sets the *short text* field. This is required for the [TYPE_SHORT_TEXT] type,
+         * is optional for the [TYPE_RANGED_VALUE] and [TYPE_NO_PERMISSION] types, and
+         * is not valid for any other type.
+         *
+         * The value must be provided as a [ComplicationText] object, so that
+         * time-dependent values may be included.
+         *
+         * The length of the text, including any time-dependent values, should not exceed seven
+         * characters. If it does, the text may be truncated by the watch face or might not fit in
+         * the complication.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setShortText(shortText: ComplicationText?) =
+            apply { putOrRemoveField(FIELD_SHORT_TEXT, shortText) }
+
+        /**
+         * Sets the *icon* field. This is required for the [TYPE_ICON] type, and is
+         * optional for the [TYPE_SHORT_TEXT], [TYPE_LONG_TEXT], [TYPE_RANGED_VALUE], and
+         * [TYPE_NO_PERMISSION] types.
+         *
+         * The provided image must be single-color, so that watch faces can tint it as required.
+         *
+         * If the icon provided here is not suitable for display in ambient mode with burn-in
+         * protection (e.g. if it includes solid blocks of pixels), then a burn-in safe version of
+         * the icon must be provided via [setBurnInProtectionIcon].
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setIcon(icon: Icon?) = apply { putOrRemoveField(FIELD_ICON, icon) }
+
+        /**
+         * Sets the burn-in protection version of the *icon* field. This should be provided if
+         * the *icon* field is provided, unless the main icon is already safe for use with
+         * burn-in protection.  This icon should have fewer lit pixels, and should use darker
+         * colors to prevent LCD burn in issues.
+         *
+         * The provided image must be single-color, so that watch faces can tint it as required.
+         *
+         * The provided image must not contain solid blocks of pixels - it should instead be
+         * composed of outlines or lines only.
+         *
+         * If this field is set, the *icon* field must also be set.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setBurnInProtectionIcon(icon: Icon?) =
+            apply { putOrRemoveField(FIELD_ICON_BURN_IN_PROTECTION, icon) }
+
+        /**
+         * Sets the *small image* field. This is required for the [TYPE_SMALL_IMAGE]
+         * type, and is optional for the [TYPE_LONG_TEXT] type.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setSmallImage(smallImage: Icon?) =
+            apply { putOrRemoveField(FIELD_SMALL_IMAGE, smallImage) }
+
+        /**
+         * Sets the burn-in protection version of the *small image* field. This should be
+         * provided if the *small image* field is provided, unless the main small image is
+         * already safe for use with burn-in protection.
+         *
+         * The provided image must not contain solid blocks of pixels - it should instead be
+         * composed of outlines or lines only.
+         *
+         * If this field is set, the *small image* field must also be set.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setBurnInProtectionSmallImage(smallImage: Icon?) =
+            apply { putOrRemoveField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, smallImage) }
+
+        /**
+         * Sets the display style for this complication data. This is valid only for types that
+         * contain small images, i.e. [TYPE_SMALL_IMAGE] and [TYPE_LONG_TEXT].
+         *
+         * This affects how watch faces will draw the image in the complication.
+         *
+         * If not specified, the default is [IMAGE_STYLE_PHOTO].
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         * @see .IMAGE_STYLE_PHOTO which can be cropped but not recolored.
+         *
+         * @see .IMAGE_STYLE_ICON which can be recolored but not cropped.
+         */
+        fun setSmallImageStyle(@ImageStyle imageStyle: Int) =
+            apply { putIntField(FIELD_IMAGE_STYLE, imageStyle) }
+
+        /**
+         * Sets the *large image* field. This is required for the [TYPE_LARGE_IMAGE]
+         * type, and is not valid for any other type.
+         *
+         * The provided image should be suitably sized to fill the screen of the watch.
+         *
+         * Returns this Builder to allow chaining.
+         *
+         * @throws IllegalStateException if this field is not valid for the complication type
+         */
+        fun setLargeImage(largeImage: Icon?) =
+            apply { putOrRemoveField(FIELD_LARGE_IMAGE, largeImage) }
+
+        /**
+         * Sets the list style hint
+         *
+         * Valid only if the type of this complication data is [EXP_TYPE_LIST]. Otherwise
+         * returns
+         * zero.
+         */
+        fun setListStyleHint(listStyleHint: Int) =
+            apply { putIntField(EXP_FIELD_LIST_STYLE_HINT, listStyleHint) }
+
+        /**
+         * Sets the *tap action* field. This is optional for any non-empty type.
+         *
+         * The provided [PendingIntent] may be fired if the complication is tapped on. Note
+         * that some complications might not be tappable, in which case this field will be ignored.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setTapAction(pendingIntent: PendingIntent?) =
+            apply { putOrRemoveField(FIELD_TAP_ACTION, pendingIntent) }
+
+        /**
+         * Sets the *content description* field for accessibility. This is optional for any
+         * non-empty type. It is recommended to provide a content description whenever the
+         * data includes an image.
+         *
+         * The provided text will be read aloud by a Text-to-speech converter for users who may
+         * be vision-impaired. It will be read aloud in addition to any long, short, or range text
+         * in the complication.
+         *
+         * If using to describe an image/icon that is purely stylistic and doesn't convey any
+         * information to the user, you may set the image content description to an empty string
+         * ("").
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setContentDescription(description: ComplicationText?) =
+            apply { putOrRemoveField(FIELD_CONTENT_DESCRIPTION, description) }
+
+        /**
+         * Sets whether or not this ComplicationData has been serialized.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setTapActionLostDueToSerialization(tapActionLostDueToSerialization: Boolean) = apply {
+            if (tapActionLostDueToSerialization) {
+                fields.putBoolean(FIELD_TAP_ACTION_LOST, tapActionLostDueToSerialization)
+            }
+        }
+
+        /**
+         * Sets the placeholder.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setPlaceholder(placeholder: ComplicationData?) = apply {
+            if (placeholder == null) {
+                fields.remove(FIELD_PLACEHOLDER_FIELDS)
+                fields.remove(FIELD_PLACEHOLDER_TYPE)
+            } else {
+                checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)
+                fields.putBundle(FIELD_PLACEHOLDER_FIELDS, placeholder.fields)
+                putIntField(FIELD_PLACEHOLDER_TYPE, placeholder.type)
+            }
+        }
+
+        /**
+         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
+         * ComplicationData. Generally this field should be set and is only nullable for backwards
+         * compatibility.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setDataSource(provider: ComponentName?) =
+            apply { putOrRemoveField(FIELD_DATA_SOURCE, provider) }
+
+        /**
+         * Sets the ambient proto layout associated with this complication.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setAmbientLayout(ambientProtoLayout: ByteArray) =
+            apply { putByteArrayField(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout) }
+
+        /**
+         * Sets the proto layout associated with this complication.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setInteractiveLayout(protoLayout: ByteArray) =
+            apply { putByteArrayField(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout) }
+
+        /**
+         * Sets the proto layout resources associated with this complication.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setLayoutResources(resources: ByteArray) =
+            apply { putByteArrayField(EXP_FIELD_PROTO_LAYOUT_RESOURCES, resources) }
+
+        /**
+         * Optional. Sets the color the color ramp should be drawn with.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setColorRamp(colorRamp: IntArray?) =
+            apply { putOrRemoveField(FIELD_COLOR_RAMP, colorRamp) }
+
+        /**
+         * Optional. Sets whether or not the color ramp should be smoothly shaded or drawn with
+         * steps.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setColorRampIsSmoothShaded(isSmoothShaded: Boolean?) =
+            apply { putOrRemoveField(FIELD_COLOR_RAMP_INTERPOLATED, isSmoothShaded) }
+
+        /**
+         * Sets the list of [ComplicationData] timeline entries.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setListEntryCollection(
+            timelineEntries: Collection<ComplicationData>?
+        ) = apply {
+            if (timelineEntries == null) {
+                fields.remove(EXP_FIELD_LIST_ENTRIES)
+            } else {
+                fields.putParcelableArray(
+                    EXP_FIELD_LIST_ENTRIES,
+                    timelineEntries.map { data ->
+                        data.fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, data.type)
+                        data.fields
+                    }.toTypedArray()
+                )
+            }
+        }
+
+        /**
+         * Sets the element weights for this complication.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setElementWeights(elementWeights: FloatArray?) =
+            apply { putOrRemoveField(FIELD_ELEMENT_WEIGHTS, elementWeights) }
+
+        /**
+         * Sets the element colors for this complication.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setElementColors(elementColors: IntArray?) =
+            apply { putOrRemoveField(FIELD_ELEMENT_COLORS, elementColors) }
+
+        /**
+         * Sets the background color to use between elements for this complication.
+         *
+         * Returns this Builder to allow chaining.
+         */
+        fun setElementBackgroundColor(@ColorInt elementBackgroundColor: Int) =
+            apply { putOrRemoveField(FIELD_ELEMENT_BACKGROUND_COLOR, elementBackgroundColor) }
+
+        /**
+         * Constructs and returns [ComplicationData] with the provided fields. All required
+         * fields must be populated before this method is called.
+         *
+         * @throws IllegalStateException if the required fields have not been populated
+         */
+        fun build(): ComplicationData {
+            // Validate.
+            for (requiredField in REQUIRED_FIELDS[type]!!) {
+                check(fields.containsKey(requiredField)) {
+                    "Field $requiredField is required for type $type"
+                }
+                check(
+                    !(fields.containsKey(FIELD_ICON_BURN_IN_PROTECTION) &&
+                        !fields.containsKey(FIELD_ICON))
+                ) {
+                    "Field ICON must be provided when field ICON_BURN_IN_PROTECTION is provided."
+                }
+                check(
+                    !(fields.containsKey(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION) &&
+                        !fields.containsKey(FIELD_SMALL_IMAGE))
+                ) {
+                    "Field SMALL_IMAGE must be provided when field SMALL_IMAGE_BURN_IN_PROTECTION" +
+                        " is provided."
+                }
+            }
+            return ComplicationData(this)
+        }
+
+        private fun putIntField(field: String, value: Int) {
+            checkFieldValidForType(field, type)
+            fields.putInt(field, value)
+        }
+
+        private fun putFloatField(field: String, value: Float) {
+            checkFieldValidForType(field, type)
+            fields.putFloat(field, value)
+        }
+
+        private fun putByteArrayField(field: String, value: ByteArray) {
+            checkFieldValidForType(field, type)
+            fields.putByteArray(field, value)
+        }
+
+        /** Sets the field with obj or removes it if null. */
+        private fun putOrRemoveField(field: String, obj: Any?) {
+            checkFieldValidForType(field, type)
+            if (obj == null) {
+                fields.remove(field)
+                return
+            }
+            when (obj) {
+                is Boolean -> fields.putBoolean(field, obj)
+                is Int -> fields.putInt(field, obj)
+                is String -> fields.putString(field, obj)
+                is Parcelable -> fields.putParcelable(field, obj)
+                is IntArray -> fields.putIntArray(field, obj)
+                is FloatArray -> fields.putFloatArray(field, obj)
+                else -> throw IllegalArgumentException("Unexpected object type: " + obj.javaClass)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "ComplicationData"
+        const val PLACEHOLDER_STRING = "__placeholder__"
+
+        /**
+         * Type sent when a complication does not have a provider configured. The system will send
+         * data of this type to watch faces when the user has not chosen a provider for an active
+         * complication, and the watch face has not set a default provider. Providers cannot send
+         * data of this type.
+         *
+         * No fields may be populated for complication data of this type.
+         */
+        const val TYPE_NOT_CONFIGURED = 1
+
+        /**
+         * Type sent when the user has specified that an active complication should have no
+         * provider, i.e. when the user has chosen "Empty" in the provider chooser. Providers cannot
+         * send data of this type.
+         *
+         * No fields may be populated for complication data of this type.
+         */
+        const val TYPE_EMPTY = 2
+
+        /**
+         * Type that can be sent by any provider, regardless of the configured type, when the
+         * provider has no data to be displayed. Watch faces may choose whether to render this in
+         * some way or leave the slot empty.
+         *
+         * No fields may be populated for complication data of this type.
+         */
+        const val TYPE_NO_DATA = 10
+
+        /**
+         * Type used for complications where the primary piece of data is a short piece of text
+         * (expected to be no more than seven characters in length). The short text may be
+         * accompanied by an icon or a short title (or both, but if both are provided then a watch
+         * face may choose to display only one).
+         *
+         * The *short text* field is required for this type, and is expected to always be
+         * displayed.
+         *
+         * The *icon* (and *burnInProtectionIcon*) and *short title* fields are
+         * optional for this type. If only one of these is provided, it is expected that it will be
+         * displayed. If both are provided, it is expected that one of these will be displayed.
+         */
+        const val TYPE_SHORT_TEXT = 3
+
+        /**
+         * Type used for complications where the primary piece of data is a piece of text. The text
+         * may be accompanied by an icon and/or a title.
+         *
+         * The *long text* field is required for this type, and is expected to always be
+         * displayed.
+         *
+         * The *long title* field is optional for this type. If provided, it is expected that
+         * this field will be displayed.
+         *
+         * The *icon* (and *burnInProtectionIcon*) and *small image* fields are also
+         * optional for this type. If provided, at least one of these should be displayed.
+         */
+        const val TYPE_LONG_TEXT = 4
+
+        /**
+         * Type used for complications including a numerical value within a range, such as a
+         * percentage. The value may be accompanied by an icon and/or short text and title.
+         *
+         * The *value*, *min value*, and *max value* fields are required for this
+         * type, and the value within the range is expected to always be displayed.
+         *
+         * The *icon* (and *burnInProtectionIcon*), *short title*, and *short
+         * text* fields are optional for this type, but at least one must be defined. The watch face
+         * may choose which of these fields to display, if any.
+         */
+        const val TYPE_RANGED_VALUE = 5
+
+        /**
+         * Type used for complications which consist only of a tintable icon.
+         *
+         * The *icon* field is required for this type, and is expected to always be displayed,
+         * unless the device is in ambient mode with burn-in protection enabled, in which case the
+         * *burnInProtectionIcon* field should be used instead.
+         *
+         * The contentDescription field is recommended for this type. Use it to describe what data
+         * the icon represents. If the icon is purely stylistic, and does not convey any information
+         * to the user, then enter the empty string as the contentDescription.
+         *
+         * No other fields are valid for this type.
+         */
+        const val TYPE_ICON = 6
+
+        /**
+         * Type used for complications which consist only of a small image.
+         *
+         * The *small image* field is required for this type, and is expected to always be
+         * displayed, unless the device is in ambient mode, in which case either nothing or the
+         * *burnInProtectionSmallImage* field may be used instead.
+         *
+         * The contentDescription field is recommended for this type. Use it to describe what data
+         * the image represents. If the image is purely stylistic, and does not convey any
+         * information to the user, then enter the empty string as the contentDescription.
+         *
+         * No other fields are valid for this type.
+         */
+        const val TYPE_SMALL_IMAGE = 7
+
+        /**
+         * Type used for complications which consist only of a large image. A large image here is
+         * one that could be used to fill the watch face, for example as the background.
+         *
+         * The *large image* field is required for this type, and is expected to always be
+         * displayed, unless the device is in ambient mode.
+         *
+         * The contentDescription field is recommended for this type. Use it to describe what data
+         * the image represents. If the image is purely stylistic, and does not convey any
+         * information to the user, then enter the empty string as the contentDescription.
+         *
+         * No other fields are valid for this type.
+         */
+        const val TYPE_LARGE_IMAGE = 8
+
+        /**
+         * Type sent by the system when the watch face does not have permission to receive
+         * complication data.
+         *
+         * Fields will be populated to allow the data to be rendered as if it were of
+         * [TYPE_SHORT_TEXT] or [TYPE_ICON] for consistency and convenience, but watch faces may
+         * render this as they see fit.
+         *
+         * It is recommended that, where possible, tapping on the complication when in this state
+         * should trigger a permission request.
+         */
+        const val TYPE_NO_PERMISSION = 9
+
+        /**
+         * Type used for complications which indicate progress towards a goal. The value may be
+         * accompanied by an icon and/or short text and title.
+         *
+         * The *value*, and *target value* fields are required for this type, and the
+         * value is expected to always be displayed. The value must be >= 0 and may be > target
+         * value. E.g. 15000 out of a target of 10000 steps.
+         *
+         * The *icon* (and *burnInProtectionIcon*), *short title*, and *short
+         * text* fields are optional for this type, but at least one must be defined. The watch face
+         * may choose which of these fields to display, if any.
+         */
+        const val TYPE_GOAL_PROGRESS = 13
+
+        /**
+         * Type used for complications to display a series of weighted values e.g. in a pie chart.
+         * The weighted values may be accompanied by an icon and/or short text and title.
+         *
+         * The *element weights* and *element colors* fields are required for this type,
+         * and the value within the range is expected to always be displayed.
+         *
+         * The *icon* (and *burnInProtectionIcon*), *short title*, and *short
+         * text* fields are optional for this type, but at least one must be defined. The watch face
+         * may choose which of these fields to display, if any.
+         */
+        const val TYPE_WEIGHTED_ELEMENTS = 14
+
+        // The following types are experimental, and they have negative IDs.
+        /** Type that specifies a proto layout based complication. */
+        const val EXP_TYPE_PROTO_LAYOUT = -11
+
+        /** Type that specifies a list of complication values. E.g. to support linear 3. */
+        const val EXP_TYPE_LIST = -12
+
+        /**
+         * Style for small images which are photos that are expected to fill the space available.
+         * Images of this style may be cropped to fit the shape of the complication - in particular,
+         * the image may be cropped to a circle. Photos my not be recolored.
+         *
+         * This is the default value.
+         */
+        const val IMAGE_STYLE_PHOTO = 1
+
+        /**
+         * Style for small images that have a transparent background and are expected to be drawn
+         * entirely within the space available, such as a launcher icon. Watch faces may add padding
+         * when drawing these images, but should never crop these images. Icons may be recolored to
+         * fit the complication style.
+         */
+        const val IMAGE_STYLE_ICON = 2
+        private const val FIELD_COLOR_RAMP = "COLOR_RAMP"
+        private const val FIELD_COLOR_RAMP_INTERPOLATED = "COLOR_RAMP_INTERPOLATED"
+        private const val FIELD_DATA_SOURCE = "FIELD_DATA_SOURCE"
+        private const val FIELD_DISPLAY_POLICY = "DISPLAY_POLICY"
+        private const val FIELD_ELEMENT_BACKGROUND_COLOR = "ELEMENT_BACKGROUND_COLOR"
+        private const val FIELD_ELEMENT_COLORS = "ELEMENT_COLORS"
+        private const val FIELD_ELEMENT_WEIGHTS = "ELEMENT_WEIGHTS"
+        private const val FIELD_END_TIME = "END_TIME"
+        private const val FIELD_ICON = "ICON"
+        private const val FIELD_ICON_BURN_IN_PROTECTION = "ICON_BURN_IN_PROTECTION"
+        private const val FIELD_IMAGE_STYLE = "IMAGE_STYLE"
+        private const val FIELD_LARGE_IMAGE = "LARGE_IMAGE"
+        private const val FIELD_LONG_TITLE = "LONG_TITLE"
+        private const val FIELD_LONG_TEXT = "LONG_TEXT"
+        private const val FIELD_MAX_VALUE = "MAX_VALUE"
+        private const val FIELD_MIN_VALUE = "MIN_VALUE"
+        private const val FIELD_PERSISTENCE_POLICY = "PERSISTENCE_POLICY"
+        private const val FIELD_PLACEHOLDER_FIELDS = "PLACEHOLDER_FIELDS"
+        private const val FIELD_PLACEHOLDER_TYPE = "PLACEHOLDER_TYPE"
+        private const val FIELD_SMALL_IMAGE = "SMALL_IMAGE"
+        private const val FIELD_SMALL_IMAGE_BURN_IN_PROTECTION = "SMALL_IMAGE_BURN_IN_PROTECTION"
+        private const val FIELD_SHORT_TITLE = "SHORT_TITLE"
+        private const val FIELD_SHORT_TEXT = "SHORT_TEXT"
+        private const val FIELD_START_TIME = "START_TIME"
+        private const val FIELD_TAP_ACTION = "TAP_ACTION"
+        private const val FIELD_TAP_ACTION_LOST = "FIELD_TAP_ACTION_LOST"
+        private const val FIELD_TARGET_VALUE = "TARGET_VALUE"
+        private const val FIELD_TIMELINE_START_TIME = "TIMELINE_START_TIME"
+        private const val FIELD_TIMELINE_END_TIME = "TIMELINE_END_TIME"
+        private const val FIELD_TIMELINE_ENTRIES = "TIMELINE"
+        private const val FIELD_TIMELINE_ENTRY_TYPE = "TIMELINE_ENTRY_TYPE"
+        private const val FIELD_VALUE = "VALUE"
+        private const val FIELD_VALUE_TYPE = "VALUE_TYPE"
+
+        // Experimental fields, these are subject to change without notice.
+        private const val EXP_FIELD_LIST_ENTRIES = "EXP_LIST_ENTRIES"
+        private const val EXP_FIELD_LIST_ENTRY_TYPE = "EXP_LIST_ENTRY_TYPE"
+        private const val EXP_FIELD_LIST_STYLE_HINT = "EXP_LIST_STYLE_HINT"
+        private const val EXP_FIELD_PROTO_LAYOUT_AMBIENT = "EXP_FIELD_PROTO_LAYOUT_AMBIENT"
+        private const val EXP_FIELD_PROTO_LAYOUT_INTERACTIVE = "EXP_FIELD_PROTO_LAYOUT_INTERACTIVE"
+        private const val EXP_FIELD_PROTO_LAYOUT_RESOURCES = "EXP_FIELD_PROTO_LAYOUT_RESOURCES"
+
+        // Originally it was planned to support both content and image content descriptions.
+        private const val FIELD_CONTENT_DESCRIPTION = "IMAGE_CONTENT_DESCRIPTION"
+
+        // The set of valid types.
+        private val VALID_TYPES: Set<Int> = setOf(
+            TYPE_NOT_CONFIGURED,
+            TYPE_EMPTY,
+            TYPE_SHORT_TEXT,
+            TYPE_LONG_TEXT,
+            TYPE_RANGED_VALUE,
+            TYPE_ICON,
+            TYPE_SMALL_IMAGE,
+            TYPE_LARGE_IMAGE,
+            TYPE_NO_PERMISSION,
+            TYPE_NO_DATA,
+            EXP_TYPE_PROTO_LAYOUT,
+            EXP_TYPE_LIST,
+            TYPE_GOAL_PROGRESS,
+            TYPE_WEIGHTED_ELEMENTS,
+        )
+
+        // Used for validation. REQUIRED_FIELDS[i] is a list containing all the fields which must be
+        // populated for @ComplicationType i.
+        private val REQUIRED_FIELDS: Map<Int, Set<String>> = mapOf(
+            TYPE_NOT_CONFIGURED to setOf(),
+            TYPE_EMPTY to setOf(),
+            TYPE_SHORT_TEXT to setOf(FIELD_SHORT_TEXT),
+            TYPE_LONG_TEXT to setOf(FIELD_LONG_TEXT),
+            TYPE_RANGED_VALUE to setOf(FIELD_VALUE, FIELD_MIN_VALUE, FIELD_MAX_VALUE),
+            TYPE_ICON to setOf(FIELD_ICON),
+            TYPE_SMALL_IMAGE to setOf(FIELD_SMALL_IMAGE, FIELD_IMAGE_STYLE),
+            TYPE_LARGE_IMAGE to setOf(FIELD_LARGE_IMAGE),
+            TYPE_NO_PERMISSION to setOf(),
+            TYPE_NO_DATA to setOf(),
+            EXP_TYPE_PROTO_LAYOUT to setOf(
+                EXP_FIELD_PROTO_LAYOUT_AMBIENT,
+                EXP_FIELD_PROTO_LAYOUT_INTERACTIVE,
+                EXP_FIELD_PROTO_LAYOUT_RESOURCES
+            ),
+            EXP_TYPE_LIST to setOf(EXP_FIELD_LIST_ENTRIES),
+            TYPE_GOAL_PROGRESS to setOf(FIELD_VALUE, FIELD_TARGET_VALUE),
+            TYPE_WEIGHTED_ELEMENTS to setOf(
+                FIELD_ELEMENT_WEIGHTS,
+                FIELD_ELEMENT_COLORS,
+                FIELD_ELEMENT_BACKGROUND_COLOR
+            ),
+        )
+
+        // Used for validation. OPTIONAL_FIELDS[i] is an array containing all the fields which are
+        // valid but not required for type i.
+        private val OPTIONAL_FIELDS: Map<Int, Set<String>> = mapOf(
+            TYPE_NOT_CONFIGURED to setOf(),
+            TYPE_EMPTY to setOf(),
+            TYPE_SHORT_TEXT to setOf(
+                FIELD_SHORT_TITLE,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_LONG_TEXT to setOf(
+                FIELD_LONG_TITLE,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_RANGED_VALUE to setOf(
+                FIELD_SHORT_TEXT,
+                FIELD_SHORT_TITLE,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_COLOR_RAMP,
+                FIELD_COLOR_RAMP_INTERPOLATED,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY,
+                FIELD_VALUE_TYPE
+            ),
+            TYPE_ICON to setOf(
+                FIELD_TAP_ACTION,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_SMALL_IMAGE to setOf(
+                FIELD_TAP_ACTION,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_LARGE_IMAGE to setOf(
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_NO_PERMISSION to setOf(
+                FIELD_SHORT_TEXT,
+                FIELD_SHORT_TITLE,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_NO_DATA to setOf(
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_LARGE_IMAGE,
+                FIELD_LONG_TEXT,
+                FIELD_LONG_TITLE,
+                FIELD_MAX_VALUE,
+                FIELD_MIN_VALUE,
+                FIELD_PLACEHOLDER_FIELDS,
+                FIELD_PLACEHOLDER_TYPE,
+                FIELD_SHORT_TEXT,
+                FIELD_SHORT_TITLE,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_TAP_ACTION,
+                FIELD_VALUE,
+                FIELD_VALUE_TYPE,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            EXP_TYPE_PROTO_LAYOUT to setOf(
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            EXP_TYPE_LIST to setOf(
+                FIELD_TAP_ACTION,
+                EXP_FIELD_LIST_STYLE_HINT,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_GOAL_PROGRESS to setOf(
+                FIELD_SHORT_TEXT,
+                FIELD_SHORT_TITLE,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_COLOR_RAMP,
+                FIELD_COLOR_RAMP_INTERPOLATED,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+            TYPE_WEIGHTED_ELEMENTS to setOf(
+                FIELD_SHORT_TEXT,
+                FIELD_SHORT_TITLE,
+                FIELD_ICON,
+                FIELD_ICON_BURN_IN_PROTECTION,
+                FIELD_SMALL_IMAGE,
+                FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+                FIELD_IMAGE_STYLE,
+                FIELD_TAP_ACTION,
+                FIELD_CONTENT_DESCRIPTION,
+                FIELD_DATA_SOURCE,
+                FIELD_PERSISTENCE_POLICY,
+                FIELD_DISPLAY_POLICY
+            ),
+        )
+
+        @JvmField
+        val CREATOR = object : Parcelable.Creator<ComplicationData> {
+            override fun createFromParcel(source: Parcel) = ComplicationData(source)
+
+            override fun newArray(size: Int): Array<ComplicationData?> = Array(size) { null }
+        }
+
+        fun isFieldValidForType(field: String, @ComplicationType type: Int): Boolean {
+            val requiredFields = REQUIRED_FIELDS[type] ?: return false
+            for (requiredField in requiredFields) {
+                if (requiredField == field) {
+                    return true
+                }
+            }
+            for (optionalField in OPTIONAL_FIELDS[type]!!) {
+                if (optionalField == field) {
+                    return true
+                }
+            }
+            return false
+        }
+
+        private fun isTypeSupported(type: Int) = type in VALID_TYPES
+
+        /** The unparceling logic needs to remain backward compatible. */
+        internal fun checkFieldValidForTypeWithoutThrowingException(
+            field: String,
+            @ComplicationType type: Int,
+        ) {
+            if (!isTypeSupported(type)) {
+                Log.w(TAG, "Type $type can not be recognized")
+                return
+            }
+            if (!isFieldValidForType(field, type)) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Field $field is not supported for type $type")
+                }
+            }
+        }
+
+        internal fun checkFieldValidForType(field: String, @ComplicationType type: Int) {
+            check(isTypeSupported(type)) { "Type $type can not be recognized" }
+            check(isFieldValidForType(field, type)) {
+                "Field $field is not supported for type $type"
+            }
+        }
+
+        /** Returns whether or not we should redact complication data in toString(). */
+        @JvmStatic
+        fun shouldRedact() = !Log.isLoggable(TAG, Log.DEBUG)
+
+        @JvmStatic
+        fun maybeRedact(unredacted: CharSequence?): String =
+            if (unredacted == null) "(null)"
+            else maybeRedact(unredacted.toString())
+
+        @JvmSynthetic
+        private fun maybeRedact(unredacted: String): String =
+            if (!shouldRedact() || unredacted == PLACEHOLDER_STRING) unredacted
+            else "REDACTED"
+    }
+}
\ No newline at end of file
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt
index c5463bf..5558fca 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt
@@ -194,7 +194,7 @@
     internal fun addToWireComplicationData(builder: WireComplicationDataBuilder) = builder.apply {
         setSmallImage(image)
         setSmallImageStyle(
-            when (type) {
+            when (this@SmallImage.type) {
                 SmallImageType.ICON -> WireComplicationData.IMAGE_STYLE_ICON
                 SmallImageType.PHOTO -> WireComplicationData.IMAGE_STYLE_PHOTO
             }
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt
index c70e7a6..513af6b 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt
@@ -21,7 +21,7 @@
 import android.graphics.Color
 import android.graphics.drawable.Icon
 import android.support.wearable.complications.ComplicationData
-import android.support.wearable.complications.ComplicationData.IMAGE_STYLE_ICON
+import android.support.wearable.complications.ComplicationData.Companion.IMAGE_STYLE_ICON
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant