Merge "Import translations. DO NOT MERGE ANYWHERE" into androidx-main
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt
index ace3165..959758d 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt
@@ -30,14 +30,14 @@
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public object Outputs {
+object Outputs {
 
     private val formatter: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss")
 
     /**
      * The intended output directory that respects the `additionalTestOutputDir`.
      */
-    public val outputDirectory: File
+    val outputDirectory: File
 
     /**
      * The usable output directory, given permission issues with `adb shell` on Android R.
@@ -46,7 +46,7 @@
      * This dir can be read/written by app
      * This dir can be read by shell (see [forceFilesForShellAccessible] for API 21/22!)
      */
-    public val dirUsableByAppAndShell: File
+    val dirUsableByAppAndShell: File
 
     /**
      * Any file created by this process for the shell to use must be explicitly made filesystem
@@ -69,10 +69,12 @@
                 // Media directory. (b/216588251)
                 context.getFirstMountedMediaDir()
             }
+
             Build.VERSION.SDK_INT <= 22 -> {
                 // prior to API 23, shell didn't have access to externalCacheDir
                 context.cacheDir
             }
+
             else -> context.externalCacheDir
         } ?: throw IllegalStateException(
             "Unable to select a directory for writing files, " +
@@ -94,6 +96,9 @@
 
         Log.d(BenchmarkState.TAG, "Output Directory: $outputDirectory")
         outputDirectory.mkdirs()
+
+        // Clear all the existing files in the output directories
+        deleteFiles { true }
     }
 
     /**
@@ -104,7 +109,7 @@
      *
      * @return The absolute path of the output [File].
      */
-    public fun writeFile(
+    fun writeFile(
         fileName: String,
         reportKey: String,
         reportOnRunEndOnly: Boolean = false,
@@ -134,22 +139,22 @@
         return destination.absolutePath
     }
 
-    public fun sanitizeFilename(filename: String): String {
+    fun sanitizeFilename(filename: String): String {
         return filename
             .replace(" ", "")
             .replace("(", "[")
             .replace(")", "]")
     }
 
-    public fun testOutputFile(filename: String): File {
+    fun testOutputFile(filename: String): File {
         return File(outputDirectory, filename)
     }
 
-    public fun dateToFileName(date: Date = Date()): String {
+    fun dateToFileName(date: Date = Date()): String {
         return formatter.format(date)
     }
 
-    public fun relativePathFor(path: String): String {
+    fun relativePathFor(path: String): String {
         val hasOutputDirectoryPrefix = path.startsWith(outputDirectory.absolutePath)
         val relativePath = when {
             hasOutputDirectoryPrefix -> path.removePrefix("${outputDirectory.absolutePath}/")
@@ -160,4 +165,10 @@
         }
         return relativePath
     }
+
+    fun deleteFiles(filterBlock: (File) -> (Boolean)) {
+        listOf(outputDirectory, dirUsableByAppAndShell)
+            .flatMap { it.listFiles(filterBlock)?.asList() ?: emptyList() }
+            .forEach { it.delete() }
+    }
 }
diff --git a/benchmark/benchmark-macro-junit4/api/current.txt b/benchmark/benchmark-macro-junit4/api/current.txt
index 0bcc71e..94459b9 100644
--- a/benchmark/benchmark-macro-junit4/api/current.txt
+++ b/benchmark/benchmark-macro-junit4/api/current.txt
@@ -4,7 +4,7 @@
   @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
     ctor public BaselineProfileRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
diff --git a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
index bd292b7..f2b4517 100644
--- a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
@@ -4,12 +4,12 @@
   @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
     ctor public BaselineProfileRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_current.txt b/benchmark/benchmark-macro-junit4/api/restricted_current.txt
index 0bcc71e..94459b9 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_current.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_current.txt
@@ -4,7 +4,7 @@
   @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
     ctor public BaselineProfileRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
     method public void collectBaselineProfile(String packageName, optional int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
index 12dca06..8a0d906 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
@@ -125,7 +125,7 @@
         iterations: Int = 3,
         outputFilePrefix: String? = null,
         includeInStartupProfile: Boolean = false,
-        filterPredicate: ((String) -> Boolean)? = null,
+        filterPredicate: ((String) -> Boolean) = { true },
         profileBlock: MacrobenchmarkScope.() -> Unit
     ) {
         collectBaselineProfile(
@@ -170,7 +170,7 @@
         outputFilePrefix: String? = null,
         includeInStartupProfile: Boolean = false,
         strictStability: Boolean = false,
-        filterPredicate: ((String) -> Boolean)? = null,
+        filterPredicate: ((String) -> Boolean) = { true },
         profileBlock: MacrobenchmarkScope.() -> Unit
     ) {
         collectStableBaselineProfile(
diff --git a/benchmark/benchmark-macro/api/restricted_current.ignore b/benchmark/benchmark-macro/api/restricted_current.ignore
index 870da85..0e7f13d 100644
--- a/benchmark/benchmark-macro/api/restricted_current.ignore
+++ b/benchmark/benchmark-macro/api/restricted_current.ignore
@@ -1,6 +1,8 @@
 // Baseline format: 1.0
 RemovedClass: androidx.benchmark.macro.Api29Kt:
     Removed class androidx.benchmark.macro.Api29Kt
+RemovedClass: androidx.benchmark.macro.BaselineProfilesKt:
+    Removed class androidx.benchmark.macro.BaselineProfilesKt
 RemovedClass: androidx.benchmark.macro.FrameTimingGfxInfoMetric:
     Removed class androidx.benchmark.macro.FrameTimingGfxInfoMetric
 RemovedClass: androidx.benchmark.macro.IdeSummaryStringKt:
@@ -17,9 +19,5 @@
     Removed class androidx.benchmark.macro.TagKt
 
 
-RemovedMethod: androidx.benchmark.macro.BaselineProfilesKt#collectBaselineProfile(String, String, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit>):
-    Removed method androidx.benchmark.macro.BaselineProfilesKt.collectBaselineProfile(String,String,kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit>)
-
-
 RemovedPackage: androidx.benchmark.macro.perfetto:
     Removed package androidx.benchmark.macro.perfetto
diff --git a/benchmark/benchmark-macro/api/restricted_current.txt b/benchmark/benchmark-macro/api/restricted_current.txt
index a5eae3b..3954702 100644
--- a/benchmark/benchmark-macro/api/restricted_current.txt
+++ b/benchmark/benchmark-macro/api/restricted_current.txt
@@ -9,11 +9,6 @@
     enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
   }
 
-  public final class BaselineProfilesKt {
-    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectStableBaselineProfile(String uniqueName, String packageName, int stableIterations, int maxIterations, optional boolean strictStability, boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectStableBaselineProfile(String uniqueName, String packageName, int stableIterations, int maxIterations, boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-  }
-
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class BatteryCharge {
     method public boolean hasMinimumCharge(optional boolean throwOnMissingMetrics);
     field public static final androidx.benchmark.macro.BatteryCharge INSTANCE;
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt
index 2c4eb75..8c5199a 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt
@@ -36,21 +36,19 @@
             HSPLjava/io/DataOutputStream;->writeByte(I)V+]Ljava/io/OutputStream;missing_types
         """.trimIndent()
 
-        val filtered = filterProfileRulesToTargetP(profile, sortRules = false)
+        val filtered = filterProfileRulesToTargetP(profile, sortRules = false) { true }
         assertEquals(filtered.lines().size, 1)
         assertEquals("Landroidx/Foo/Bar;", filtered)
     }
 
     @Test
     fun filterBaselineRulesWithSorting() {
-        // https://youtrack.jetbrains.com/issue/KT-2425
-        val dollar = "$"
         val profile = """
             HPLandroidx/lifecycle/Lifecycle${dollar}Event;->downFrom(Landroidx/lifecycle/Lifecycle${dollar}State;)Landroidx/lifecycle/Lifecycle${dollar}Event;
             HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
             HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;->invokeCallbacks(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle${dollar}Event;Ljava/lang/Object;)V
         """.trimIndent()
-        val sorted = filterProfileRulesToTargetP(profile, sortRules = true)
+        val sorted = filterProfileRulesToTargetP(profile, sortRules = true) { true }
         assertEquals(sorted.lines().size, 3)
         val expected = """
             HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
@@ -61,6 +59,24 @@
     }
 
     @Test
+    fun filterBaselineRulesWithSortingAndCustomFilter() {
+        val profile = """
+            HPLandroidx/lifecycle/Lifecycle${dollar}Event;->downFrom(Landroidx/lifecycle/Lifecycle${dollar}State;)Landroidx/lifecycle/Lifecycle${dollar}Event;
+            HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;->invokeCallbacks(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle${dollar}Event;Ljava/lang/Object;)V
+            HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
+        """.trimIndent()
+        val sorted = filterProfileRulesToTargetP(profile, sortRules = true) {
+            it.startsWith("HSPL")
+        }
+        assertEquals(sorted.lines().size, 2)
+        val expected = """
+            HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
+            HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;->invokeCallbacks(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle${dollar}Event;Ljava/lang/Object;)V
+        """.trimIndent()
+        assertEquals(expected, sorted)
+    }
+
+    @Test
     fun deviceSpecifier() {
         if (DeviceInfo.isEmulator) {
             assertEquals(deviceSpecifier, "-e ")
@@ -70,4 +86,8 @@
             assertNotEquals(deviceSpecifier, "-s  ")
         }
     }
+    companion object {
+        // https://youtrack.jetbrains.com/issue/KT-2425
+        const val dollar = "$"
+    }
 }
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
index d19339e..7ce8f71 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
@@ -16,6 +16,7 @@
 
 package androidx.benchmark.macro
 
+import android.annotation.SuppressLint
 import android.os.Build
 import android.util.Log
 import androidx.annotation.RequiresApi
@@ -37,13 +38,12 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @RequiresApi(28)
-@JvmOverloads
 fun collectBaselineProfile(
     uniqueName: String,
     packageName: String,
     iterations: Int = 3,
     includeInStartupProfile: Boolean,
-    filterPredicate: ((String) -> Boolean)?,
+    filterPredicate: ((String) -> Boolean),
     profileBlock: MacrobenchmarkScope.() -> Unit,
 ) {
     val scope = buildMacrobenchmarkScope(packageName)
@@ -83,12 +83,13 @@
                 runs a non-trivial amount of code.
             """.trimIndent()
         }
-        // Filter
-        val profile = filterProfileRulesToTargetP(unfilteredProfile, sortRules = true)
-        // Report
+        val profile = filterProfileRulesToTargetP(
+            profile = unfilteredProfile,
+            sortRules = true,
+            filterPredicate = filterPredicate
+        )
         reportResults(
             profile = profile,
-            filterPredicate = filterPredicate,
             uniqueFilePrefix = uniqueName,
             startTime = startTime,
             includeInStartupProfile = includeInStartupProfile
@@ -103,9 +104,8 @@
  * waiting until they are stable.
  * @suppress
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @RequiresApi(28)
-@JvmOverloads
 fun collectStableBaselineProfile(
     uniqueName: String,
     packageName: String,
@@ -113,7 +113,7 @@
     maxIterations: Int,
     strictStability: Boolean = false,
     includeInStartupProfile: Boolean,
-    filterPredicate: ((String) -> Boolean)?,
+    filterPredicate: ((String) -> Boolean),
     profileBlock: MacrobenchmarkScope.() -> Unit
 ) {
     val scope = buildMacrobenchmarkScope(packageName)
@@ -198,10 +198,13 @@
                 " invokes the target app, and runs a non-trivial amount of code"
         }
 
-        val profile = filterProfileRulesToTargetP(lastProfile, sortRules = true)
+        val profile = filterProfileRulesToTargetP(
+            profile = lastProfile,
+            sortRules = true,
+            filterPredicate = filterPredicate
+        )
         reportResults(
             profile = profile,
-            filterPredicate = filterPredicate,
             uniqueFilePrefix = uniqueName,
             startTime = startTime,
             includeInStartupProfile = includeInStartupProfile
@@ -230,6 +233,7 @@
 /**
  * Builds a function that can kill the target process using the provided [MacrobenchmarkScope].
  */
+@SuppressLint("BanThreadSleep")
 private fun MacrobenchmarkScope.killProcessBlock(): () -> Unit {
     val killProcessBlock = {
         // When generating baseline profiles we want to default to using
@@ -246,14 +250,10 @@
  */
 private fun reportResults(
     profile: String,
-    filterPredicate: ((String) -> Boolean)?,
     uniqueFilePrefix: String,
     startTime: Long,
     includeInStartupProfile: Boolean
 ) {
-    // Filter profile if necessary based on filters
-    val filteredProfile = applyPackageFilters(profile, filterPredicate)
-
     // Write a file with a timestamp to be able to disambiguate between runs with the same
     // unique name.
 
@@ -272,10 +272,10 @@
             )
         }
 
-    val absolutePath = Outputs.writeFile(fileName, reportKey) { it.writeText(filteredProfile) }
+    val absolutePath = Outputs.writeFile(fileName, reportKey) { it.writeText(profile) }
     val tsAbsolutePath = Outputs.writeFile(tsFileName, "baseline-profile-ts") {
         Log.d(TAG, "Pull Baseline Profile with: `adb pull \"${it.absolutePath}\" .`")
-        it.writeText(filteredProfile)
+        it.writeText(profile)
     }
 
     val totalRunTime = System.nanoTime() - startTime
@@ -382,7 +382,11 @@
 }
 
 @VisibleForTesting
-internal fun filterProfileRulesToTargetP(profile: String, sortRules: Boolean = true): String {
+internal fun filterProfileRulesToTargetP(
+    profile: String,
+    sortRules: Boolean = true,
+    filterPredicate: ((String) -> Boolean)
+): String {
     val rules = profile.lines()
     var filteredRules = rules.filterNot { rule ->
         // We want to filter out rules that are not supported on P. (b/216508418)
@@ -390,7 +394,8 @@
         if (rule.startsWith("[")) { // Array qualifier
             true
         } else rule.contains("+") // Inline cache specifier
-    }
+    }.filter(filterPredicate)
+
     if (sortRules) {
         filteredRules = filteredRules.mapNotNull { ProfileRule.parse(it) }
             .sortedWith(ProfileRule.comparator)
@@ -399,14 +404,6 @@
     return filteredRules.joinToString(separator = "\n")
 }
 
-private fun applyPackageFilters(profile: String, filterPredicate: ((String) -> Boolean)?): String {
-    return filterPredicate?.run {
-        profile
-            .lines()
-            .filter(filterPredicate).joinToString(System.lineSeparator())
-    } ?: profile
-}
-
 private fun summaryRecord(record: Summary): String {
     val summary = StringBuilder()
 
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
index 76d6330..dc9536e 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
@@ -16,6 +16,10 @@
 
 package androidx.bluetooth.integration.testapp.ui.scanner
 
+// TODO(ofy) Migrate from androidx.bluetooth.integration.testapp.experimental.BluetoothLe to
+// androidx.bluetooth.BluetoothDevice once in place
+// TODO(ofy) Migrate from androidx.bluetooth.integration.testapp.experimental.BluetoothLe to
+// androidx.bluetooth.BluetoothLe once scan API is in place
 import android.bluetooth.le.ScanResult
 import android.bluetooth.le.ScanSettings
 import android.os.Bundle
@@ -28,8 +32,6 @@
 import androidx.bluetooth.integration.testapp.R
 import androidx.bluetooth.integration.testapp.databinding.FragmentScannerBinding
 import android.annotation.SuppressLint
-// TODO(ofy) Migrate to androidx.bluetooth.BluetoothDevice once in place
-// TODO(ofy) Migrate to androidx.bluetooth.BluetoothLe once scan API is in place
 import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
 import androidx.bluetooth.integration.testapp.ui.common.getColor
 import androidx.core.view.isVisible
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
index c7e2ad8..c81ae66 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
@@ -23,63 +23,69 @@
 
     @Test
     fun withAnEmptyFlag_itReturnsTheDefaultValue() {
-        assertThat(KmpFlagParser.parse("")).isEqualTo(setOf(KmpPlatform.JVM))
+        assertThat(KmpFlagParser.parse("")).isEqualTo(
+            setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
+        )
     }
 
     @Test
     fun withANullFlag_itReturnsTheDefaultValue() {
-        assertThat(KmpFlagParser.parse(null)).isEqualTo(setOf(KmpPlatform.JVM))
+        assertThat(KmpFlagParser.parse(null)).isEqualTo(
+            setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
+        )
     }
 
     @Test
     fun withASingleDefaultPlatform_itParsesTheFlagCorrectly() {
-        assertThat(KmpFlagParser.parse("+jvm")).isEqualTo(setOf(KmpPlatform.JVM))
+        assertThat(KmpFlagParser.parse("+jvm")).isEqualTo(
+            setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
+        )
     }
 
     @Test
     fun withNoPlatforms_itParsesTheFlagCorrectly() {
-        assertThat(KmpFlagParser.parse("-jvm")).isEqualTo(emptySet<KmpPlatform>())
+        assertThat(KmpFlagParser.parse("-jvm,-desktop")).isEqualTo(emptySet<KmpPlatform>())
     }
 
     @Test
     fun withASingleNonDefaultPlatform_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("+js")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.JS)
+            setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.DESKTOP)
         )
     }
 
     @Test
     fun withAMultiplePlatforms_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("+js,+mac")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.MAC)
+            setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.DESKTOP)
         )
     }
 
     @Test
     fun withNegativeFlags_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("-jvm,+mac")).isEqualTo(
-            setOf(KmpPlatform.MAC)
+            setOf(KmpPlatform.MAC, KmpPlatform.DESKTOP)
         )
     }
 
     @Test
     fun withTheNativeFlag_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("+native")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.MAC, KmpPlatform.LINUX)
+            setOf(KmpPlatform.JVM, KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
         )
     }
 
     @Test
     fun withMultipleFlagsIncludingTheNativeFlag_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("-jvm,+native,+js")).isEqualTo(
-            setOf(KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.LINUX)
+            setOf(KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
         )
     }
 
     @Test
     fun withRedundentFlags_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("-jvm,+native,+linux,+mac,+linux")).isEqualTo(
-            setOf(KmpPlatform.MAC, KmpPlatform.LINUX)
+            setOf(KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
         )
     }
 }
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 6c1b432..f6c6252 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -86,6 +86,17 @@
         } else { null }
     }
 
+    @JvmOverloads
+    fun desktop(
+        block: Action<KotlinJvmTarget>? = null
+    ): KotlinJvmTarget? {
+        return if (project.enableJvm()) {
+            kotlinExtension.jvm("desktop") {
+                block?.execute(this)
+            }
+        } else { null }
+    }
+
     /**
      * Configures all mac targets supported by AndroidX.
      */
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
index e16b300..3cb172a 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
@@ -206,6 +206,11 @@
 
     // Don't check Hilt compile-only configurations
     if (name.startsWith("hiltCompileOnly")) return false
+
+    // Don't check Desktop configurations since we don't publish them anyway
+    if (name.startsWith("desktop")) return false
+    if (name.startsWith("skiko")) return false
+
     return true
 }
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
index b3be53b..d6a11f1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
@@ -23,6 +23,7 @@
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
+import org.gradle.api.attributes.Category
 import org.gradle.api.attributes.LibraryElements
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.tasks.bundling.Zip
@@ -94,6 +95,10 @@
                     LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
                     objectFactory.named(ATTRIBUTE_NAME)
                 )
+                it.attribute(
+                    Category.CATEGORY_ATTRIBUTE,
+                    objectFactory.named(Category.DOCUMENTATION)
+                )
             }
         }
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt b/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
index 590be01..1b04cf7 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
@@ -20,6 +20,7 @@
 import org.gradle.api.Project
 import org.gradle.api.attributes.Attribute
 import org.gradle.api.attributes.Usage
+import org.gradle.api.attributes.java.TargetJvmEnvironment
 
 /**
  * Creates `testAarAsJar` configuration that can be used for JVM tests that need to Android library
@@ -56,4 +57,11 @@
     project.configurations.getByName(configurationName).dependencies.add(
         project.dependencies.create(aarAsJar)
     )
+
+    // Added to allow the :external:paparazzi:paparazzi build to select the correct jar (not get
+    // confused by the mpp jars) when the mpp builds are enabled
+    project.configurations.getByName(testAarsAsJars.name).attributes.attribute(
+        TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
+        project.objects.named(TargetJvmEnvironment::class.java, TargetJvmEnvironment.ANDROID)
+    )
 }
\ No newline at end of file
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
index 802d8ef..7961e09 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
@@ -31,10 +31,11 @@
     // https://blog.jetbrains.com/kotlin/2021/10/important-ua-parser-js-exploit-and-kotlin-js/
     JS,
     MAC,
-    LINUX;
+    LINUX,
+    DESKTOP;
     companion object {
         val native = listOf(MAC, LINUX)
-        val enabledByDefault = listOf(JVM)
+        val enabledByDefault = listOf(JVM, DESKTOP)
         private const val JVM_PLATFORM = "jvm"
         private const val JS_PLATFORM = "js"
         private const val MAC_ARM_64 = "macosarm64"
@@ -43,6 +44,7 @@
         private const val IOS_SIMULATOR_ARM_64 = "iossimulatorarm64"
         private const val IOS_X_64 = "iosx64"
         private const val IOS_ARM_64 = "iosarm64"
+        private const val DESKTOP_PLATFORM = "desktop"
         val macPlatforms = listOf(MAC_ARM_64, MAC_OSX_64)
         val linuxPlatforms = listOf(LINUX_64)
         val iosPlatforms = listOf(IOS_SIMULATOR_ARM_64, IOS_ARM_64, IOS_X_64)
@@ -88,4 +90,5 @@
 fun Project.enableLinux(): Boolean =
     enabledKmpPlatforms().contains(KmpPlatform.LINUX) || Multiplatform.isKotlinNativeEnabled(this)
 fun Project.enableJvm(): Boolean = enabledKmpPlatforms().contains(KmpPlatform.JVM)
+fun Project.enableDesktop(): Boolean = enabledKmpPlatforms().contains(KmpPlatform.DESKTOP)
 fun Project.enableNative(): Boolean = enableMac() && enableLinux()
\ No newline at end of file
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
index 326c96f..554c2fdf 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
@@ -40,6 +40,7 @@
         @JvmStatic
         fun isKotlinNativeEnabled(project: Project): Boolean {
             return "KMP".equals(System.getenv()["ANDROIDX_PROJECTS"], ignoreCase = true) ||
+                "INFRAROGUE".equals(System.getenv()["ANDROIDX_PROJECTS"], ignoreCase = true) ||
                 ProjectLayoutType.isPlayground(project) ||
                 project.providers.gradleProperty("androidx.kmp.native.enabled")
                     .orNull?.toBoolean() == true
diff --git a/busytown/androidx.sh b/busytown/androidx.sh
index 621ff22..ef344d5 100755
--- a/busytown/androidx.sh
+++ b/busytown/androidx.sh
@@ -22,6 +22,8 @@
       -Pandroidx.enableComposeCompilerMetrics=true \
       -Pandroidx.enableComposeCompilerReports=true \
       -Pandroidx.constraints=true \
+      # If/when we enable desktop, enable VerifyDependencyVersionsTask.kt/shouldVerifyConfiguration
+      -Pandroidx.enabled.kmp.target.platforms=-desktop \
       --no-daemon \
       --profile "$@"; then
     EXIT_VALUE=1
diff --git a/busytown/androidx_compose_multiplatform.sh b/busytown/androidx_compose_multiplatform.sh
index 5f39449..dd31e36 100755
--- a/busytown/androidx_compose_multiplatform.sh
+++ b/busytown/androidx_compose_multiplatform.sh
@@ -9,10 +9,15 @@
 
 
 # b/235340662 don't verify dependency versions because we cannot pin to multiplatform deps
-./androidx.sh \
-  -Pandroidx.compose.multiplatformEnabled=true \
-  compileDebugAndroidTestSources \
-  compileDebugSources \
-  desktopTestClasses \
-  -x verifyDependencyVersions  \
-  -Pandroidx.enableAffectedModuleDetection=false "$@"
+impl/build.sh buildOnServer createAllArchives checkExternalLicenses listTaskOutputs \
+      -Pandroidx.compose.multiplatformEnabled=true \
+      -Pandroidx.enableComposeCompilerMetrics=true \
+      -Pandroidx.enableComposeCompilerReports=true \
+      -Pandroidx.constraints=true \
+      --no-daemon \
+      --profile \
+      compileDebugAndroidTestSources \
+      compileDebugSources \
+      desktopTestClasses \
+      -x verifyDependencyVersions \
+      -Pandroidx.enableAffectedModuleDetection=false "$@"
diff --git a/busytown/androidx_multiplatform_linux.sh b/busytown/androidx_multiplatform_linux.sh
index 3f9d518..34e9b84 100755
--- a/busytown/androidx_multiplatform_linux.sh
+++ b/busytown/androidx_multiplatform_linux.sh
@@ -3,12 +3,12 @@
 cd "$(dirname $0)"
 
 # Builds all projects that support KMP except for Compose-specific projects which are already
-# covered by androidx_compose_multiplatform.sh
+# covered by androidx_compose_multiplatform.sh.
 
 # Must be run on Linux
 
-# build just KMP projects. This will also enable native targets.
-export ANDROIDX_PROJECTS=KMP
+# build just INFRAROGUE projects. This will also enable native targets.
+export ANDROIDX_PROJECTS=INFRAROGUE   # TODO: Switch from `INFRAROGUE` to `KMP`
 
 # disable cache, NS does not allow it yet: b/235227707
 export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
diff --git a/busytown/androidx_multiplatform_mac.sh b/busytown/androidx_multiplatform_mac.sh
index 9f2dfbd..3373b9d 100755
--- a/busytown/androidx_multiplatform_mac.sh
+++ b/busytown/androidx_multiplatform_mac.sh
@@ -7,7 +7,7 @@
 
 # Must be run on Mac
 
-export ANDROIDX_PROJECTS=KMP
+export ANDROIDX_PROJECTS=INFRAROGUE   # TODO: Switch from `INFRAROGUE` to `KMP`
 
 # disable GCP cache, these machines don't have credentials.
 export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
diff --git a/busytown/androidx_multiplatform_mac_host_tests.sh b/busytown/androidx_multiplatform_mac_host_tests.sh
index 8da2ed5..77eb725 100755
--- a/busytown/androidx_multiplatform_mac_host_tests.sh
+++ b/busytown/androidx_multiplatform_mac_host_tests.sh
@@ -6,7 +6,7 @@
 
 # Must be run on Mac
 
-export ANDROIDX_PROJECTS=KMP
+export ANDROIDX_PROJECTS=INFRAROGUE   # TODO: Switch from `INFRAROGUE` to `KMP`
 
 # disable GCP cache, these machines don't have credentials.
 export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index bc9620f..0cf7c6c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -88,7 +88,8 @@
                 CameraDevice.TEMPLATE_PREVIEW
             )
 
-            CaptureType.VIDEO_CAPTURE -> sessionBuilder.setTemplateType(
+            CaptureType.VIDEO_CAPTURE,
+            CaptureType.STREAM_SHARING -> sessionBuilder.setTemplateType(
                 CameraDevice.TEMPLATE_RECORD
             )
         }
@@ -103,7 +104,8 @@
 
             CaptureType.PREVIEW,
             CaptureType.IMAGE_ANALYSIS,
-            CaptureType.VIDEO_CAPTURE ->
+            CaptureType.VIDEO_CAPTURE,
+            CaptureType.STREAM_SHARING ->
                 captureBuilder.templateType = CameraDevice.TEMPLATE_RECORD
         }
         mutableConfig.insertOption(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
index ddba7d3..5d79889 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
@@ -229,6 +229,7 @@
         override fun setZslDisabled(disabled: Boolean) = this
 
         override fun setHighResolutionDisabled(disabled: Boolean) = this
+        override fun setCaptureType(captureType: UseCaseConfigFactory.CaptureType) = this
 
         override fun build(): MeteringRepeating {
             return MeteringRepeating(cameraProperties, useCaseConfig, displayInfoManager)
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 26856ba..8c0100e 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -45,7 +45,7 @@
     testImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
     testImplementation(project(":camera:camera-video"))
     testImplementation(project(":camera:camera-testing"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.junit) // Needed for Assert.assertThrows
     testImplementation("org.codehaus.plexus:plexus-utils:3.4.1")
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
index 1f1834bf..505da1e 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
@@ -64,6 +64,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
+import android.util.Range;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
@@ -232,8 +233,6 @@
     @Before
     public void setup() throws CameraAccessException, InterruptedException,
             AssumptionViolatedException, TimeoutException, ExecutionException {
-        mTestParameters0 = new CaptureSessionTestParameters("mTestParameters0");
-        mTestParameters1 = new CaptureSessionTestParameters("mTestParameters1");
         mHandler = new Handler(sHandlerThread.getLooper());
 
         mExecutor = CameraXExecutors.newHandlerExecutor(mHandler);
@@ -254,6 +253,10 @@
         } catch (CameraAccessExceptionCompat e) {
             throw new AssumptionViolatedException("Could not retrieve camera characteristics", e);
         }
+        mTestParameters0 = new CaptureSessionTestParameters("mTestParameters0",
+                mCameraCharacteristics);
+        mTestParameters1 = new CaptureSessionTestParameters("mTestParameters1",
+                mCameraCharacteristics);
 
         mDynamicRangesCompat =
                 DynamicRangesCompat.fromCameraCharacteristics(mCameraCharacteristics);
@@ -684,8 +687,9 @@
         // From CameraEventCallbacks option
         assertThat(captureResult.getRequest().get(CaptureRequest.CONTROL_AF_MODE)).isEqualTo(
                 CaptureRequest.CONTROL_AF_MODE_MACRO);
-        assertThat(captureResult.getRequest().get(CaptureRequest.FLASH_MODE)).isEqualTo(
-                CaptureRequest.FLASH_MODE_TORCH);
+        assertThat(captureResult.getRequest().get(
+                CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(
+                mTestParameters0.mEvRange.getLower());
 
         // From SessionConfig option
         assertThat(captureResult.getRequest().get(CaptureRequest.CONTROL_AE_MODE)).isEqualTo(
@@ -856,8 +860,9 @@
                 CaptureRequest.CONTROL_AF_MODE_OFF);
 
         // From CameraEventCallbacks option
-        assertThat(captureResult.getRequest().get(CaptureRequest.FLASH_MODE)).isEqualTo(
-                CaptureRequest.FLASH_MODE_TORCH);
+        assertThat(captureResult.getRequest().get(
+                CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(
+                mTestParameters0.mEvRange.getLower());
 
         // From SessionConfig option
         assertThat(captureResult.getRequest().get(CaptureRequest.CONTROL_AE_MODE)).isEqualTo(
@@ -1032,7 +1037,8 @@
         assertThat(result1).isInstanceOf(Camera2CameraCaptureResult.class);
         CaptureResult captureResult1 = ((Camera2CameraCaptureResult) result1).getCaptureResult();
         assertThat(captureResult1.getRequest().get(
-                CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(0);
+                CaptureRequest.CONTROL_SCENE_MODE)).isEqualTo(
+                mTestParameters0.mTestCameraEventCallback.mAvailableSceneMode);
         // The onDisableSession should not been invoked.
         verify(mTestParameters0.mTestCameraEventCallback.mDisableCallback,
                 never()).onCaptureCompleted(any(CameraCaptureResult.class));
@@ -1051,7 +1057,8 @@
         assertThat(result2).isInstanceOf(Camera2CameraCaptureResult.class);
         CaptureResult captureResult2 = ((Camera2CameraCaptureResult) result2).getCaptureResult();
         assertThat(captureResult2.getRequest().get(
-                CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(0);
+                CaptureRequest.CONTROL_SCENE_MODE)).isEqualTo(
+                mTestParameters0.mTestCameraEventCallback.mAvailableSceneMode);
         // The onEnableSession should not been invoked in close().
         verify(mTestParameters0.mTestCameraEventCallback.mEnableCallback,
                 never()).onCaptureCompleted(any(CameraCaptureResult.class));
@@ -1673,31 +1680,47 @@
      */
     private static class TestCameraEventCallback extends CameraEventCallback {
 
+        TestCameraEventCallback(CameraCharacteristicsCompat characteristics) {
+            if (characteristics != null) {
+                int[] availableSceneModes =
+                        characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES);
+                if (availableSceneModes != null && availableSceneModes.length > 0) {
+                    mAvailableSceneMode = availableSceneModes[0];
+                } else {
+                    mAvailableSceneMode = CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
+                }
+            } else {
+                mAvailableSceneMode = CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
+            }
+        }
+
         private final CameraCaptureCallback mEnableCallback = Mockito.mock(
                 CameraCaptureCallback.class);
         private final CameraCaptureCallback mDisableCallback = Mockito.mock(
                 CameraCaptureCallback.class);
 
+        private final int mAvailableSceneMode;
+
         @Override
         public CaptureConfig onInitSession() {
-            return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0, null);
+            return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode, null);
         }
 
         @Override
         public CaptureConfig onEnableSession() {
-            return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0,
+            return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode,
                     mEnableCallback);
         }
 
         @Override
         public CaptureConfig onRepeating() {
-            return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0, null);
+            return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode, null);
         }
 
         @Override
         public CaptureConfig onDisableSession() {
-            return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
-                    0, mDisableCallback);
+            return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode,
+                    mDisableCallback);
         }
     }
 
@@ -1795,8 +1818,7 @@
         private final SessionConfig mSessionConfig;
         private final CaptureConfig mCaptureConfig;
 
-        private final TestCameraEventCallback mTestCameraEventCallback =
-                new TestCameraEventCallback();
+        private final TestCameraEventCallback mTestCameraEventCallback;
         private final CameraEventCallback mMockCameraEventCallback = Mockito.mock(
                 CameraEventCallback.class);
 
@@ -1810,6 +1832,7 @@
                 Mockito.mock(CameraCaptureSession.CaptureCallback.class);
 
         private final DeferrableSurface mDeferrableSurface;
+        private final Range<Integer> mEvRange;
         /**
          * A composite capture callback that dispatches callbacks to both mock and real callbacks.
          * The mock callback is used to verify the callback result. The real callback is used to
@@ -1825,7 +1848,7 @@
                             }
                         });
 
-        CaptureSessionTestParameters(String name) {
+        CaptureSessionTestParameters(String name, CameraCharacteristicsCompat characteristics) {
             mHandlerThread = new HandlerThread(name);
             mHandlerThread.start();
             mHandler = HandlerCompat.createAsync(mHandlerThread.getLooper());
@@ -1843,6 +1866,7 @@
             builder.addRepeatingCameraCaptureCallback(
                     CaptureCallbackContainer.create(mCamera2CaptureCallback));
 
+            mTestCameraEventCallback = new TestCameraEventCallback(characteristics);
             MutableOptionsBundle testCallbackConfig = MutableOptionsBundle.create();
             testCallbackConfig.insertOption(Camera2ImplConfig.CAMERA_EVENT_CALLBACK_OPTION,
                     new CameraEventCallbacks(mTestCameraEventCallback));
@@ -1856,15 +1880,19 @@
 
             // Set capture request options
             // ==================================================================================
-            // Priority | Component        | AF_MODE       | FLASH_MODE         | AE_MODE
+            // Priority | Component        | AF_MODE       | EV MODE            | AE_MODE
             // ----------------------------------------------------------------------------------
             // P1 | CaptureConfig          | AF_MODE_OFF  |                     |
             // ----------------------------------------------------------------------------------
-            // P2 | CameraEventCallbacks   | AF_MODE_MACRO | FLASH_MODE_TORCH   |
+            // P2 | CameraEventCallbacks   | AF_MODE_MACRO | Min EV             |
             // ----------------------------------------------------------------------------------
-            // P3 | SessionConfig          | AF_MODE_AUTO  | FLASH_MODE_SINGLE  | AE_MODE_ON
+            // P3 | SessionConfig          | AF_MODE_AUTO  | Max EV             | AE_MODE_ON
             // ==================================================================================
 
+            mEvRange = characteristics != null
+                    ? characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE)
+                    : new Range<>(0, 0);
+
             Camera2ImplConfig.Builder camera2ConfigBuilder = new Camera2ImplConfig.Builder();
 
             // Add capture request options for CameraEventCallbacks
@@ -1878,8 +1906,8 @@
                                             CaptureRequest.CONTROL_AF_MODE,
                                             CaptureRequest.CONTROL_AF_MODE_MACRO)
                                     .setCaptureRequestOption(
-                                            CaptureRequest.FLASH_MODE,
-                                            CaptureRequest.FLASH_MODE_TORCH)
+                                            CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
+                                            mEvRange.getLower())
                                     .build());
                     return builder.build();
                 }
@@ -1893,7 +1921,7 @@
                     .setCaptureRequestOption(
                             CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO)
                     .setCaptureRequestOption(
-                            CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE)
+                            CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, mEvRange.getUpper())
                     .setCaptureRequestOption(
                             CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
index da4070a..5481b76 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
@@ -78,6 +78,7 @@
                 sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
                 break;
             case VIDEO_CAPTURE:
+            case STREAM_SHARING:
                 sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_RECORD);
                 break;
         }
@@ -101,6 +102,7 @@
                 captureBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
                 break;
             case VIDEO_CAPTURE:
+            case STREAM_SHARING:
                 captureBuilder.setTemplateType(CameraDevice.TEMPLATE_RECORD);
                 break;
         }
@@ -125,7 +127,7 @@
         int targetRotation = mDisplayInfoManager.getMaxSizeDisplay().getRotation();
         mutableConfig.insertOption(OPTION_TARGET_ROTATION, targetRotation);
 
-        if (captureType == CaptureType.VIDEO_CAPTURE) {
+        if (captureType == CaptureType.VIDEO_CAPTURE || captureType == CaptureType.STREAM_SHARING) {
             mutableConfig.insertOption(OPTION_ZSL_DISABLED, true);
         }
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index 8e0b364..b11b2a5a 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -2127,6 +2127,8 @@
             ImageFormat.PRIVATE,
             PREVIEW_SIZE,
             SDR,
+            listOf(CaptureType.PREVIEW),
+            useCase.currentConfig,
             /*targetFrameRate=*/null
         )
         val attachedAnalysis = AttachedSurfaceInfo.create(
@@ -2137,6 +2139,8 @@
             ImageFormat.YUV_420_888,
             RECORD_SIZE,
             SDR,
+            listOf(CaptureType.IMAGE_ANALYSIS),
+            useCase.currentConfig,
             /*targetFrameRate=*/null
         )
 
@@ -2172,6 +2176,8 @@
             ImageFormat.PRIVATE,
             PREVIEW_SIZE,
             DynamicRange(FORMAT_HDR10, BIT_DEPTH_10_BIT),
+            listOf(CaptureType.PREVIEW),
+            useCase.currentConfig,
             /*targetFrameRate=*/null
         )
         val attachedPriv2 = AttachedSurfaceInfo.create(
@@ -2182,6 +2188,8 @@
             ImageFormat.YUV_420_888,
             RECORD_SIZE,
             DynamicRange(FORMAT_HDR10_PLUS, BIT_DEPTH_10_BIT),
+            listOf(CaptureType.VIDEO_CAPTURE),
+            useCase.currentConfig,
             /*targetFrameRate=*/null
         )
 
@@ -2226,6 +2234,8 @@
             ImageFormat.PRIVATE,
             PREVIEW_SIZE,
             SDR,
+            listOf(CaptureType.PREVIEW),
+            useCase.currentConfig,
             /*targetFrameRate=*/null
         )
         val attachedPriv2 = AttachedSurfaceInfo.create(
@@ -2236,6 +2246,8 @@
             ImageFormat.YUV_420_888,
             RECORD_SIZE,
             SDR,
+            listOf(CaptureType.IMAGE_ANALYSIS),
+            useCase.currentConfig,
             /*targetFrameRate=*/null
         )
 
@@ -2465,6 +2477,8 @@
             ImageFormat.JPEG,
             Size(1280, 720),
             SDR,
+            listOf(CaptureType.PREVIEW),
+            useCase.currentConfig,
             Range(40, 50)
         )
         getSuggestedSpecsAndVerify(
@@ -2492,6 +2506,8 @@
             ImageFormat.JPEG,
             Size(1280, 720),
             SDR,
+            listOf(CaptureType.PREVIEW),
+            useCase.currentConfig,
             Range(40, 50)
         )
         getSuggestedSpecsAndVerify(
@@ -2519,6 +2535,8 @@
             ImageFormat.JPEG,
             Size(1280, 720),
             SDR,
+            listOf(CaptureType.PREVIEW),
+            useCase.currentConfig,
             Range(40, 50)
         )
         getSuggestedSpecsAndVerify(
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java
index 722a214..0369ec0 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java
@@ -27,6 +27,7 @@
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
 
 import java.util.UUID;
 
@@ -56,6 +57,12 @@
         return retrieveOption(OPTION_SURFACE_OCCUPANCY_PRIORITY);
     }
 
+    @NonNull
+    @Override
+    public UseCaseConfigFactory.CaptureType getCaptureType() {
+        return UseCaseConfigFactory.CaptureType.PREVIEW;
+    }
+
     @Override
     public int getInputFormat() {
         return ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
@@ -190,5 +197,12 @@
             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
             return this;
         }
+
+        @NonNull
+        @Override
+        public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+            getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
index 32462ac..d39f3e1 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
@@ -33,6 +33,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.os.Build;
 import android.provider.MediaStore;
 import android.util.Rational;
 import android.util.Size;
@@ -100,6 +101,9 @@
 
     @Before
     public void setup() {
+        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes"
+                        + " the test failure.",
+                Build.MODEL.equalsIgnoreCase("wembley") && Build.VERSION.SDK_INT <= 30);
         FakeCamera fakeCamera = new FakeCamera("fakeCameraId");
 
         FakeCameraDeviceSurfaceManager fakeCameraDeviceSurfaceManager =
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
index 2515afd..47df690 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
@@ -157,7 +157,7 @@
             takePictureCallback,
             Futures.immediateFuture(null)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
         // Act and return.
         nodeIn.edge.accept(input)
         return if (outputFileOptions == null) {
@@ -193,7 +193,7 @@
             CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
             createJpegBytes(WIDTH, HEIGHT)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
         // Act and return.
         nodeIn.edge.accept(input)
         val filePath = takePictureCallback.getOnDiskResult().savedUri!!.path!!
@@ -232,7 +232,7 @@
             createJpegBytes(WIDTH, HEIGHT)
         )
         // Act.
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
         // Act and return.
         nodeIn.edge.accept(input)
         // Assert: the output image is identical to the input.
@@ -266,7 +266,7 @@
             takePictureCallback,
             Futures.immediateFuture(null)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
 
         // Act: send input to the edge and wait for the saved URI
         nodeIn.edge.accept(input)
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
index 60d5d804..daa23c4 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
@@ -21,10 +21,8 @@
 import android.graphics.Rect
 import android.graphics.SurfaceTexture
 import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
-import android.os.Build
 import android.util.Size
 import android.view.Surface
-import androidx.annotation.RequiresApi
 import androidx.camera.core.CameraEffect
 import androidx.camera.core.ImageProxy
 import androidx.camera.core.ImageReaderProxys
@@ -38,6 +36,7 @@
 import androidx.camera.testing.HandlerUtil
 import androidx.camera.testing.TestImageUtil.createBitmap
 import androidx.camera.testing.TestImageUtil.getAverageDiff
+import androidx.camera.testing.TestImageUtil.rotateBitmap
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.concurrent.futures.await
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -137,14 +136,13 @@
         }
     }
 
-    @RequiresApi(Build.VERSION_CODES.M)
     @Test
     fun snapshotAndRelease_futureReceivesException(): Unit = runBlocking {
         // Arrange: create DefaultSurfaceProcessor and setup input/output Surface.
         createSurfaceProcessor()
 
         // Act: take a snapshot and then release the processor.
-        val snapshotFuture = surfaceProcessor.snapshot(JPEG_QUALITY)
+        val snapshotFuture = surfaceProcessor.snapshot(JPEG_QUALITY, 0)
         surfaceProcessor.release()
 
         // Assert: the snapshot future should receive an exception.
@@ -159,7 +157,7 @@
         }
     }
 
-    @RequiresApi(Build.VERSION_CODES.M)
+    @SdkSuppress(minSdkVersion = 23)
     @Test
     fun snapshot_JpegWrittenToSurface(): Unit = runBlocking {
         // Arrange: create DefaultSurfaceProcessor and setup input/output Surface.
@@ -175,9 +173,10 @@
             format = ImageFormat.JPEG
         )
         surfaceProcessor.onOutputSurface(surfaceOutput)
+        val rotationDegrees = 90
 
-        // Act: take a snapshot and draw a Bitmap to the input Surface
-        surfaceProcessor.snapshot(JPEG_QUALITY)
+        // Act: draw a Bitmap to the input Surface and take a snapshot with 90 degrees rotation.
+        surfaceProcessor.snapshot(JPEG_QUALITY, rotationDegrees)
         val inputImage = createBitmap(WIDTH, HEIGHT)
         val inputSurface = surfaceRequest.deferrableSurface.surface.get()
         val canvas = inputSurface.lockHardwareCanvas()
@@ -190,7 +189,8 @@
         val bytes = ByteArray(byteBuffer.remaining())
         byteBuffer.get(bytes)
         val outputImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
-        assertThat(getAverageDiff(outputImage, inputImage)).isEqualTo(0)
+        val expectedImage = rotateBitmap(inputImage, rotationDegrees)
+        assertThat(getAverageDiff(outputImage, expectedImage)).isEqualTo(0)
 
         // Cleanup.
         surfaceRequest.deferrableSurface.close()
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index c3c0d6e..b7268b0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -32,6 +32,7 @@
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_SESSION_CONFIG;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
@@ -771,7 +772,7 @@
     public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
             @NonNull UseCaseConfigFactory factory) {
         Config captureConfig = factory.getConfig(
-                UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS,
+                DEFAULT_CONFIG.getConfig().getCaptureType(),
                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
 
         if (applyDefaultConfig) {
@@ -1022,7 +1023,8 @@
                     .setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
-                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
+                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
+                    .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -1600,5 +1602,13 @@
             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
             return this;
         }
+
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        @Override
+        public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+            getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 4eb88f5..b547104 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -42,6 +42,7 @@
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
@@ -513,7 +514,7 @@
     public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
             @NonNull UseCaseConfigFactory factory) {
         Config captureConfig = factory.getConfig(
-                UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE,
+                DEFAULT_CONFIG.getConfig().getCaptureType(),
                 getCaptureMode());
 
         if (applyDefaultConfig) {
@@ -2024,7 +2025,8 @@
             Builder builder = new Builder()
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
-                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
+                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
+                    .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -3077,5 +3079,16 @@
             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
             return this;
         }
+
+        /**
+         * {@inheritDoc}
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        @Override
+        public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+            getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 02d7366..153f731 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -39,6 +39,7 @@
 import static androidx.camera.core.impl.PreviewConfig.OPTION_TARGET_ROTATION;
 import static androidx.camera.core.impl.PreviewConfig.OPTION_USE_CASE_EVENT_CALLBACK;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_FRAME_RATE;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
@@ -582,7 +583,7 @@
     public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
             @NonNull UseCaseConfigFactory factory) {
         Config captureConfig = factory.getConfig(
-                UseCaseConfigFactory.CaptureType.PREVIEW,
+                DEFAULT_CONFIG.getConfig().getCaptureType(),
                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
 
         if (applyDefaultConfig) {
@@ -780,7 +781,8 @@
             Builder builder = new Builder()
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
-                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
+                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
+                    .setCaptureType(UseCaseConfigFactory.CaptureType.PREVIEW);
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
 
@@ -1245,5 +1247,13 @@
             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
             return this;
         }
+
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        @Override
+        public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+            getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
index a14dfdf..ee8e36c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
@@ -143,8 +143,7 @@
         inputEdge.getRequestEdge().setListener(requestConsumer);
         inputEdge.getErrorEdge().setListener(this::sendCaptureError);
 
-        mOutputEdge = Out.of(inputEdge.getInputFormat(), inputEdge.getOutputFormat(),
-                inputEdge.isVirtualCamera());
+        mOutputEdge = Out.of(inputEdge.getInputFormat(), inputEdge.getOutputFormat());
         return mOutputEdge;
     }
 
@@ -399,14 +398,9 @@
          */
         abstract int getOutputFormat();
 
-        /**
-         * Whether the pipeline is connected to a virtual camera.
-         */
-        abstract boolean isVirtualCamera();
-
-        static Out of(int inputFormat, int outputFormat, boolean isVirtualCamera) {
+        static Out of(int inputFormat, int outputFormat) {
             return new AutoValue_CaptureNode_Out(new Edge<>(), new Edge<>(), inputFormat,
-                    outputFormat, isVirtualCamera);
+                    outputFormat);
         }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
index 318f126..4109418 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
@@ -74,9 +74,7 @@
                 throw new ImageCaptureException(ERROR_FILE_IO, "Failed to extract EXIF data.", e);
             }
         }
-        if (EXIF_ROTATION_AVAILABILITY.shouldUseExifOrientation(image)
-                && !inputPacket.isVirtualCamera()) {
-            // Virtual camera doesn't respect the CaptureRequest rotation degrees.
+        if (EXIF_ROTATION_AVAILABILITY.shouldUseExifOrientation(image)) {
             checkNotNull(exif, "JPEG image must have exif.");
             return createPacketWithHalRotation(request, exif, image);
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
index ba7717d..c55f5ce 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
@@ -233,15 +233,9 @@
         @NonNull
         abstract ImageProxy getImageProxy();
 
-        /**
-         * Whether the pipeline is connected to a virtual camera.
-         */
-        abstract boolean isVirtualCamera();
-
         static InputPacket of(@NonNull ProcessingRequest processingRequest,
-                @NonNull ImageProxy imageProxy, boolean isVirtualCamera) {
-            return new AutoValue_ProcessingNode_InputPacket(processingRequest, imageProxy,
-                    isVirtualCamera);
+                @NonNull ImageProxy imageProxy) {
+            return new AutoValue_ProcessingNode_InputPacket(processingRequest, imageProxy);
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
index f9d7238..481fe56 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
@@ -43,12 +43,10 @@
 
     ProcessingRequest mPendingRequest;
     private ProcessingNode.In mOutputEdge;
-    private boolean mIsVirtualCamera;
 
     @NonNull
     @Override
     public ProcessingNode.In transform(@NonNull CaptureNode.Out captureNodeOut) {
-        mIsVirtualCamera = captureNodeOut.isVirtualCamera();
         // Listen to input edges.
         captureNodeOut.getImageEdge().setListener(this::matchImageWithRequest);
         captureNodeOut.getRequestEdge().setListener(this::trackIncomingRequest);
@@ -97,8 +95,7 @@
                         mPendingRequest.getTagBundleKey()));
         checkState(stageId == mPendingRequest.getStageIds().get(0));
 
-        mOutputEdge.getEdge().accept(
-                ProcessingNode.InputPacket.of(mPendingRequest, imageProxy, mIsVirtualCamera));
+        mOutputEdge.getEdge().accept(ProcessingNode.InputPacket.of(mPendingRequest, imageProxy));
         mPendingRequest = null;
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java
index cdde2a1..8fe27de 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java
@@ -25,6 +25,9 @@
 import androidx.camera.core.DynamicRange;
 
 import com.google.auto.value.AutoValue;
+
+import java.util.List;
+
 /**
  * Container object for holding {@link SurfaceConfig} and its attributed ImageFormat,
  * {@link Size}, and target Frame Rate {@link Range}
@@ -45,9 +48,11 @@
             int imageFormat,
             @NonNull Size size,
             @NonNull DynamicRange dynamicRange,
+            @NonNull List<UseCaseConfigFactory.CaptureType> captureTypes,
+            @Nullable Config implementationOptions,
             @Nullable Range<Integer> targetFrameRate) {
         return new AutoValue_AttachedSurfaceInfo(surfaceConfig, imageFormat, size,
-                dynamicRange, targetFrameRate);
+                dynamicRange, captureTypes, implementationOptions, targetFrameRate);
     }
 
     /** Returns the SurfaceConfig. */
@@ -65,6 +70,16 @@
     @NonNull
     public abstract DynamicRange getDynamicRange();
 
+    /** Returns the capture types of this surface. Multiple capture types represent a
+     *  {@link androidx.camera.core.streamsharing.StreamSharing} and its children.*/
+    @SuppressWarnings("AutoValueImmutableFields")
+    @NonNull
+    public abstract List<UseCaseConfigFactory.CaptureType> getCaptureTypes();
+
+    /** Returns the implementations of this surface. */
+    @Nullable
+    public abstract Config getImplementationOptions();
+
     /** Returns the configuration target frame rate. */
     @Nullable
     public abstract Range<Integer> getTargetFrameRate();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java
index 57a4094..7e1b2a2 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java
@@ -64,4 +64,34 @@
                 DynamicRange.UNSPECIFIED));
     }
 
+    /**
+     * Returns whether a dynamic range was set on this config.
+     *
+     * <p>This method can be used to determine whether a dynamic range has been set to override
+     * the default {@link DynamicRange#UNSPECIFIED}.
+     */
+    default boolean hasDynamicRange() {
+        return containsOption(OPTION_INPUT_DYNAMIC_RANGE);
+    }
+
+    /**
+     * Builder for a {@link ImageInputConfig}.
+     *
+     * @param <B> The top level builder type for which this builder is composed with.
+     */
+    interface Builder<B> {
+        /**
+         * Sets the dynamic range required for images from this configuration.
+         *
+         * <p>Valid values depend on the specific configuration. The default behavior when not
+         * set is to automatically choose a dynamic range based on device capabilities and the
+         * dynamic range requested by other use cases, but use cases should override that
+         * behavior if needed.
+         *
+         * @param dynamicRange The dynamic range required for this configuration.
+         * @return The current Builder.
+         */
+        @NonNull
+        B setDynamicRange(@NonNull DynamicRange dynamicRange);
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java
index 43d3f9e..7e13965 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java
@@ -94,6 +94,12 @@
     Option<Boolean> OPTION_HIGH_RESOLUTION_DISABLED =
             Option.create("camerax.core.useCase.highResolutionDisabled", boolean.class);
 
+    /**
+     * Option: camerax.core.useCase.highResolutionDisabled
+     */
+    Option<UseCaseConfigFactory.CaptureType> OPTION_CAPTURE_TYPE = Option.create(
+            "camerax.core.useCase.captureType", UseCaseConfigFactory.CaptureType.class);
+
 
     // *********************************************************************************************
 
@@ -314,6 +320,14 @@
     }
 
     /**
+     * @return The {@link UseCaseConfigFactory.CaptureType} of this UseCaseConfig.
+     */
+    @NonNull
+    default UseCaseConfigFactory.CaptureType getCaptureType() {
+        return retrieveOption(OPTION_CAPTURE_TYPE);
+    }
+
+    /**
      * Builder for a {@link UseCase}.
      *
      * @param <T> The type of the object which will be built by {@link #build()}.
@@ -420,6 +434,14 @@
         B setHighResolutionDisabled(boolean disabled);
 
         /**
+         * Sets the capture type for this configuration.
+         *
+         * @param captureType The capture type for this use case.
+         */
+        @NonNull
+        B setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType);
+
+        /**
          * Retrieves the configuration used by this builder.
          *
          * @return the configuration used by this builder.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java
index 209a9aa..480eb29 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java
@@ -50,7 +50,12 @@
         /**
          * Capture type for video capture. A use case of this type is consuming a stream of frames.
          */
-        VIDEO_CAPTURE
+        VIDEO_CAPTURE,
+
+        /**
+         * Capture type for stream sharing. A use case of this type is consuming a stream of frames.
+         */
+        STREAM_SHARING
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 4e74b5e..2350fbc 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -609,6 +609,8 @@
             existingSurfaces.add(AttachedSurfaceInfo.create(surfaceConfig,
                     useCase.getImageFormat(), useCase.getAttachedSurfaceResolution(),
                     Preconditions.checkNotNull(useCase.getAttachedStreamSpec()).getDynamicRange(),
+                    getCaptureTypes(useCase),
+                    useCase.getAttachedStreamSpec().getImplementationOptions(),
                     useCase.getCurrentConfig().getTargetFrameRate(null)));
             suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec());
         }
@@ -672,6 +674,19 @@
         }
     }
 
+    @NonNull
+    private static List<UseCaseConfigFactory.CaptureType> getCaptureTypes(UseCase useCase) {
+        List<UseCaseConfigFactory.CaptureType> result = new ArrayList<>();
+        if (isStreamSharing(useCase)) {
+            for (UseCase child : ((StreamSharing) useCase).getChildren()) {
+                result.add(child.getCurrentConfig().getCaptureType());
+            }
+        } else {
+            result.add(useCase.getCurrentConfig().getCaptureType());
+        }
+        return result;
+    }
+
     /**
      * Sets effects on the given {@link UseCase} list and returns unused effects.
      */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
index bfce459..1eea449 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
@@ -18,6 +18,7 @@
 
 import static androidx.camera.core.ImageProcessingUtil.writeJpegBytesToSurface;
 import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+import static androidx.camera.core.impl.utils.TransformUtils.rotateSize;
 import static androidx.core.util.Preconditions.checkState;
 
 import static java.util.Objects.requireNonNull;
@@ -28,7 +29,6 @@
 import android.opengl.Matrix;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.util.Pair;
 import android.util.Size;
 import android.view.Surface;
 
@@ -42,17 +42,21 @@
 import androidx.camera.core.SurfaceOutput;
 import androidx.camera.core.SurfaceProcessor;
 import androidx.camera.core.SurfaceRequest;
+import androidx.camera.core.impl.utils.MatrixExt;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.Futures;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.util.Supplier;
 
+import com.google.auto.value.AutoValue;
 import com.google.common.util.concurrent.ListenableFuture;
 
 import kotlin.Triple;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -90,8 +94,7 @@
     // Only access this on GL thread.
     private boolean mIsReleased = false;
     // Only access this on GL thread.
-    private final List<Pair<CallbackToFutureAdapter.Completer<Void>, Integer>> mPendingSnapshots =
-            new ArrayList<>();
+    private final List<PendingSnapshot> mPendingSnapshots = new ArrayList<>();
 
     /** Constructs {@link DefaultSurfaceProcessor} with default shaders. */
     DefaultSurfaceProcessor() {
@@ -180,13 +183,15 @@
         });
     }
 
-    @NonNull
     @Override
-    public ListenableFuture<Void> snapshot(@IntRange(from = 0, to = 100) int jpegQuality) {
+    @NonNull
+    public ListenableFuture<Void> snapshot(
+            @IntRange(from = 0, to = 100) int jpegQuality,
+            @IntRange(from = 0, to = 359) int rotationDegrees) {
         return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(
                 completer -> {
-                    Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot =
-                            new Pair<>(completer, jpegQuality);
+                    PendingSnapshot pendingSnapshot = PendingSnapshot.of(jpegQuality,
+                            rotationDegrees, completer);
                     executeSafely(
                             () -> mPendingSnapshots.add(pendingSnapshot),
                             () -> completer.setException(new Exception(
@@ -240,6 +245,7 @@
      *
      * @param jpegOutput The <Surface, Surface size, transform matrix> tuple for drawing.
      */
+    @WorkerThread
     private void takeSnapshotAndDrawJpeg(@Nullable Triple<Surface, Size, float[]> jpegOutput) {
         if (mPendingSnapshots.isEmpty()) {
             // No pending snapshot requests, do nothing.
@@ -252,48 +258,72 @@
             return;
         }
 
-        // Get snapshot Bitmap.
-        Bitmap bitmap = getBitmap(jpegOutput.getSecond(), jpegOutput.getThird());
-
         // Write to JPEG surface, once for each snapshot request.
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        byte[] jpegBytes = null;
-        int jpegQuality = -1;
-        for (Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot :
-                mPendingSnapshots) {
-            if (jpegQuality != pendingSnapshot.second) {
-                // If the quality is different, re-encode the bitmap.
-                outputStream.reset();
-                bitmap.compress(Bitmap.CompressFormat.JPEG, pendingSnapshot.second, outputStream);
-                jpegBytes = outputStream.toByteArray();
-                jpegQuality = pendingSnapshot.second;
+        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+            byte[] jpegBytes = null;
+            int jpegQuality = -1;
+            int rotationDegrees = -1;
+            Bitmap bitmap = null;
+            Iterator<PendingSnapshot> iterator = mPendingSnapshots.iterator();
+            while (iterator.hasNext()) {
+                PendingSnapshot pendingSnapshot = iterator.next();
+                // Take a new snapshot if the rotation is different.
+                if (rotationDegrees != pendingSnapshot.getRotationDegrees() || bitmap == null) {
+                    rotationDegrees = pendingSnapshot.getRotationDegrees();
+                    // Recycle the previous bitmap to free up memory.
+                    if (bitmap != null) {
+                        bitmap.recycle();
+                    }
+                    bitmap = getBitmap(jpegOutput.getSecond(), jpegOutput.getThird(),
+                            rotationDegrees);
+                    // Clear JPEG quality to force re-encoding.
+                    jpegQuality = -1;
+                }
+                // Re-encode the bitmap if the quality is different.
+                if (jpegQuality != pendingSnapshot.getJpegQuality()) {
+                    outputStream.reset();
+                    jpegQuality = pendingSnapshot.getJpegQuality();
+                    bitmap.compress(Bitmap.CompressFormat.JPEG, jpegQuality, outputStream);
+                    jpegBytes = outputStream.toByteArray();
+                }
+                writeJpegBytesToSurface(jpegOutput.getFirst(), requireNonNull(jpegBytes));
+                pendingSnapshot.getCompleter().set(null);
+                iterator.remove();
             }
-            writeJpegBytesToSurface(jpegOutput.getFirst(), requireNonNull(jpegBytes));
-            pendingSnapshot.first.set(null);
+        } catch (IOException e) {
+            failAllPendingSnapshots(e);
         }
-        mPendingSnapshots.clear();
     }
 
     private void failAllPendingSnapshots(@NonNull Throwable throwable) {
-        for (Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot :
-                mPendingSnapshots) {
-            pendingSnapshot.first.setException(throwable);
+        for (PendingSnapshot pendingSnapshot : mPendingSnapshots) {
+            pendingSnapshot.getCompleter().setException(throwable);
         }
         mPendingSnapshots.clear();
     }
 
     @NonNull
-    private Bitmap getBitmap(@NonNull Size size, @NonNull float[] textureTransform) {
+    private Bitmap getBitmap(@NonNull Size size,
+            @NonNull float[] textureTransform,
+            int rotationDegrees) {
         // Flip the snapshot. This is for reverting the GL transform added in SurfaceOutputImpl.
         float[] snapshotTransform = new float[16];
         // TODO(b/278109696): move GL flipping to MatrixExt.
         Matrix.setIdentityM(snapshotTransform, 0);
         Matrix.translateM(snapshotTransform, 0, 0f, 1f, 0f);
         Matrix.scaleM(snapshotTransform, 0, 1f, -1f, 1f);
+
+        // Rotate the output if requested.
+        MatrixExt.preRotate(snapshotTransform, rotationDegrees, 0.5f, 0.5f);
+
+        // Apply the texture transform.
         Matrix.multiplyMM(snapshotTransform, 0, snapshotTransform, 0, textureTransform, 0);
+
+        // Update the size based on the rotation degrees.
+        size = rotateSize(size, rotationDegrees);
+
         // Take a snapshot Bitmap and compress it to JPEG.
         return mGlRenderer.snapshot(size, snapshotTransform);
-
     }
 
     @WorkerThread
@@ -303,9 +333,8 @@
             for (SurfaceOutput surfaceOutput : mOutputSurfaces.keySet()) {
                 surfaceOutput.close();
             }
-            for (Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot :
-                    mPendingSnapshots) {
-                pendingSnapshot.first.setException(
+            for (PendingSnapshot pendingSnapshot : mPendingSnapshots) {
+                pendingSnapshot.getCompleter().setException(
                         new Exception("Failed to snapshot: DefaultSurfaceProcessor is released."));
             }
             mOutputSurfaces.clear();
@@ -362,6 +391,31 @@
     }
 
     /**
+     * A pending snapshot request to be executed on the next frame available.
+     */
+    @AutoValue
+    abstract static class PendingSnapshot {
+
+        @IntRange(from = 0, to = 100)
+        abstract int getJpegQuality();
+
+        @IntRange(from = 0, to = 359)
+        abstract int getRotationDegrees();
+
+        @NonNull
+        abstract CallbackToFutureAdapter.Completer<Void> getCompleter();
+
+        @NonNull
+        static AutoValue_DefaultSurfaceProcessor_PendingSnapshot of(
+                @IntRange(from = 0, to = 100) int jpegQuality,
+                @IntRange(from = 0, to = 359) int rotationDegrees,
+                @NonNull CallbackToFutureAdapter.Completer<Void> completer) {
+            return new AutoValue_DefaultSurfaceProcessor_PendingSnapshot(
+                    jpegQuality, rotationDegrees, completer);
+        }
+    }
+
+    /**
      * Factory class that produces {@link DefaultSurfaceProcessor}.
      *
      * <p> This is for working around the limit that OpenGL cannot be initialized in unit tests.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java
index 0dd1f6da..3b512c3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java
@@ -52,7 +52,9 @@
      * Takes a snapshot of the next available frame and write it to JPEG outputs.
      */
     @NonNull
-    default ListenableFuture<Void> snapshot(@IntRange(from = 0, to = 100) int jpegQuality) {
+    default ListenableFuture<Void> snapshot(
+            @IntRange(from = 0, to = 100) int jpegQuality,
+            @IntRange(from = 0, to = 359) int rotationDegrees) {
         return Futures.immediateFuture(null);
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java
index 862de4e..8eb4fe1 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java
@@ -22,6 +22,7 @@
 
 import android.os.Build;
 
+import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
@@ -100,7 +101,9 @@
 
     @NonNull
     @Override
-    public ListenableFuture<Void> snapshot(int jpegQuality) {
+    public ListenableFuture<Void> snapshot(
+            @IntRange(from = 0, to = 100) int jpegQuality,
+            @IntRange(from = 0, to = 359) int rotationDegrees) {
         return immediateFailedFuture(
                 new Exception("Snapshot not supported by external SurfaceProcessor"));
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
index 4ba6851..93cd629 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
@@ -20,6 +20,7 @@
 import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE;
 import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
 import static androidx.core.util.Preconditions.checkNotNull;
 
@@ -30,6 +31,7 @@
 import android.os.Build;
 import android.util.Size;
 
+import androidx.annotation.IntRange;
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -66,7 +68,6 @@
  */
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 public class StreamSharing extends UseCase {
-
     @NonNull
     private static final StreamSharingConfig DEFAULT_CONFIG;
 
@@ -89,6 +90,8 @@
         MutableConfig mutableConfig = new StreamSharingBuilder().getMutableConfig();
         mutableConfig.insertOption(OPTION_INPUT_FORMAT,
                 ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
+        mutableConfig.insertOption(OPTION_CAPTURE_TYPE,
+                UseCaseConfigFactory.CaptureType.STREAM_SHARING);
         DEFAULT_CONFIG = new StreamSharingConfig(OptionsBundle.from(mutableConfig));
     }
 
@@ -102,10 +105,11 @@
             @NonNull UseCaseConfigFactory useCaseConfigFactory) {
         super(DEFAULT_CONFIG);
         mVirtualCamera = new VirtualCamera(parentCamera, children, useCaseConfigFactory,
-                jpegQuality -> {
+                (jpegQuality, rotationDegrees) -> {
                     SurfaceProcessorNode sharingNode = mSharingNode;
                     if (sharingNode != null) {
-                        return sharingNode.getSurfaceProcessor().snapshot(jpegQuality);
+                        return sharingNode.getSurfaceProcessor().snapshot(
+                                jpegQuality, rotationDegrees);
                     } else {
                         return Futures.immediateFailedFuture(new Exception(
                                 "Failed to take picture: pipeline is not ready."));
@@ -119,7 +123,7 @@
             @NonNull UseCaseConfigFactory factory) {
         // The shared stream optimizes for VideoCapture.
         Config captureConfig = factory.getConfig(
-                UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE,
+                DEFAULT_CONFIG.getCaptureType(),
                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
 
         if (applyDefaultConfig) {
@@ -312,7 +316,9 @@
          * Takes a snapshot of the current stream and write it to the children with JPEG Surface.
          */
         @NonNull
-        ListenableFuture<Void> jpegSnapshot(int jpegQuality);
+        ListenableFuture<Void> jpegSnapshot(
+                @IntRange(from = 0, to = 100) int jpegQuality,
+                @IntRange(from = 0, to = 359) int rotationDegrees);
     }
 
     @VisibleForTesting
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
index 0ae8daa..d701527 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.core.streamsharing;
 
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
 import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_CLASS;
 import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_NAME;
 
@@ -23,6 +24,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.CaptureConfig;
@@ -31,6 +33,7 @@
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
 import androidx.camera.core.internal.TargetConfig;
 
 import java.util.UUID;
@@ -162,4 +165,13 @@
             @NonNull UseCase.EventCallback eventCallback) {
         throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
     }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    @Override
+    public StreamSharingBuilder setCaptureType(
+            @NonNull UseCaseConfigFactory.CaptureType captureType) {
+        getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+        return this;
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
index 294173d..cdbd616 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
@@ -41,6 +41,7 @@
 public class VirtualCameraControl extends ForwardingCameraControl {
 
     private static final int DEFAULT_JPEG_QUALITY = 100;
+    private static final int DEFAULT_ROTATION_DEGREES = 0;
 
     private final StreamSharing.Control mStreamSharingControl;
 
@@ -57,12 +58,18 @@
             @ImageCapture.CaptureMode int captureMode,
             @ImageCapture.FlashType int flashType) {
         checkArgument(captureConfigs.size() == 1, "Only support one capture config.");
-        int jpegQuality = getJpegQuality(captureConfigs.get(0));
-        return Futures.allAsList(singletonList(mStreamSharingControl.jpegSnapshot(jpegQuality)));
+        return Futures.allAsList(singletonList(mStreamSharingControl.jpegSnapshot(
+                getJpegQuality(captureConfigs.get(0)),
+                getRotationDegrees(captureConfigs.get(0)))));
     }
 
     private int getJpegQuality(@NonNull CaptureConfig captureConfig) {
         return requireNonNull(captureConfig.getImplementationOptions().retrieveOption(
                 CaptureConfig.OPTION_JPEG_QUALITY, DEFAULT_JPEG_QUALITY));
     }
+
+    private int getRotationDegrees(@NonNull CaptureConfig captureConfig) {
+        return requireNonNull(captureConfig.getImplementationOptions().retrieveOption(
+                CaptureConfig.OPTION_ROTATION, DEFAULT_ROTATION_DEGREES));
+    }
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
index 34b1b4a..eb21888 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
@@ -17,7 +17,6 @@
 package androidx.camera.core.imagecapture
 
 import android.graphics.ImageFormat
-import android.graphics.Matrix
 import android.graphics.Rect
 import android.hardware.camera2.CameraDevice
 import android.os.Build
@@ -52,11 +51,9 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.impl.utils.futures.Futures
 import androidx.camera.core.internal.IoConfig.OPTION_IO_EXECUTOR
-import androidx.camera.core.processing.Packet
 import androidx.camera.testing.TestImageUtil.createJpegBytes
 import androidx.camera.testing.TestImageUtil.createJpegFakeImageProxy
 import androidx.camera.testing.TestImageUtil.createYuvFakeImageProxy
-import androidx.camera.testing.fakes.FakeCameraCaptureResult
 import androidx.camera.testing.fakes.FakeImageInfo
 import androidx.camera.testing.fakes.FakeImageReaderProxy
 import androidx.camera.testing.fakes.GrayscaleImageEffect
@@ -161,37 +158,6 @@
     }
 
     @Test
-    fun createPipelineWithVirtualCamera_plumbedToProcessingInput2PacketOperation() {
-        // Arrange: create a pipeline with a virtual camera.
-        val pipeline = ImagePipeline(
-            imageCaptureConfig,
-            SIZE,
-            GrayscaleImageEffect(),
-            true
-        )
-        // Listen to the input to packet operation.
-        var isVirtualCamera = false
-        pipeline.processingNode.injectProcessingInput2Packet {
-            isVirtualCamera = it.isVirtualCamera
-            return@injectProcessingInput2Packet Packet.of(
-                it.imageProxy,
-                null,
-                it.imageProxy.cropRect,
-                it.imageProxy.format,
-                Matrix(),
-                FakeCameraCaptureResult()
-            )
-        }
-
-        // Act: send in-memory request.
-        sendInMemoryRequest(pipeline)
-
-        // Assert: the input packet is marked as from a virtual camera.
-        assertThat(isVirtualCamera).isTrue()
-        pipeline.close()
-    }
-
-    @Test
     fun createRequests_verifyCameraRequest() {
         // Arrange.
         val captureInput = imagePipeline.captureNode.inputEdge
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
index 096fea8..4d024c5 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
@@ -65,7 +65,7 @@
             HEIGHT
         )
         val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image)
 
         // Act.
         val output = operation.apply(input)
@@ -89,7 +89,7 @@
         }
         val image = createJpegFakeImageProxy(jpegBytes)
         val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image)
 
         // Act.
         val output = operation.apply(input)
@@ -121,7 +121,7 @@
             FakeTakePictureCallback(),
             Futures.immediateFuture(null)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image)
 
         // Act.
         val output = operation.apply(input)
@@ -146,25 +146,7 @@
         injectRotationOptionQuirk()
         val image = createJpegFakeImageProxy(createJpegBytes(WIDTH, HEIGHT))
         val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
-
-        // Act.
-        val output = operation.apply(input)
-
-        // Assert: the metadata are based on Packet only.
-        assertThat(output.cropRect).isEqualTo(CROP_RECT)
-        assertThat(output.rotationDegrees).isEqualTo(ROTATION_DEGREES)
-        assertThat(output.format).isEqualTo(ImageFormat.JPEG)
-        assertThat(output.size).isEqualTo(Size(WIDTH, HEIGHT))
-        assertThat(output.sensorToBufferTransform).isEqualTo(SENSOR_TO_BUFFER)
-    }
-
-    @Test
-    fun isVirtualCamera_outputIgnoresExifRotation() {
-        // Arrange: create input
-        val image = createJpegFakeImageProxy(createJpegBytes(WIDTH, HEIGHT))
-        val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image, true)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image)
 
         // Act.
         val output = operation.apply(input)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
index 8710e8b..a2281b2 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
@@ -81,7 +81,7 @@
         // Act: process the request.
         val jpegBytes = createJpegBytes(WIDTH, HEIGHT)
         val image = createJpegFakeImageProxy(jpegBytes)
-        processingNodeIn.edge.accept(ProcessingNode.InputPacket.of(request, image, false))
+        processingNodeIn.edge.accept(ProcessingNode.InputPacket.of(request, image))
         shadowOf(getMainLooper()).idle()
 
         // Assert: the image is not saved.
@@ -94,7 +94,7 @@
         val takePictureCallback = FakeTakePictureCallback()
         val image = FakeImageProxy(FakeImageInfo())
         val processingRequest = createProcessingRequest(takePictureCallback)
-        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image)
 
         // Act: send input to the edge and wait for callback
         processingNodeIn.edge.accept(input)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
index 94b0c42..a99697e 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
@@ -44,7 +44,7 @@
 
     @Before
     fun setUp() {
-        captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, ImageFormat.JPEG, false)
+        captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, ImageFormat.JPEG)
         matchingNodeOut = node.transform(captureNodeOut)
         matchingNodeOut.edge.setListener {
             packetPropagated.add(it)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
index eab2744..8a3d600 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
@@ -20,6 +20,8 @@
 import android.util.Range
 import android.util.Size
 import androidx.camera.core.DynamicRange
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.testing.fakes.FakeUseCaseConfig
 import com.google.common.truth.Truth
 import org.junit.Before
 import org.junit.Test
@@ -40,11 +42,23 @@
     private val imageFormat = ImageFormat.JPEG
     private val size = Size(1920, 1080)
     private val dynamicRange = DynamicRange.SDR
+    private val captureTypes = listOf(CaptureType.PREVIEW)
+    private val inputFormat = ImageFormat.PRIVATE
     private val targetFramerate = Range(10, 20)
+    private val config = FakeUseCaseConfig.Builder(
+        CaptureType.PREVIEW,
+        inputFormat
+    ).useCaseConfig.config
+
     @Before
     fun setup() {
         attachedSurfaceInfo = AttachedSurfaceInfo.create(
-            surfaceConfig, imageFormat, size, dynamicRange,
+            surfaceConfig,
+            imageFormat,
+            size,
+            dynamicRange,
+            captureTypes,
+            config,
             targetFramerate
         )
     }
@@ -74,6 +88,28 @@
     }
 
     @Test
+    fun canGetCaptureTypes() {
+        Truth.assertThat(attachedSurfaceInfo!!.captureTypes.size).isEqualTo(captureTypes.size)
+        for ((index, value) in captureTypes.withIndex()) {
+            Truth.assertThat(attachedSurfaceInfo!!.captureTypes[index]).isEqualTo(value)
+        }
+    }
+
+    @Test
+    fun canGetImplementationOption() {
+        Truth.assertThat(
+            attachedSurfaceInfo!!.implementationOptions!!
+                .containsOption(ImageInputConfig.OPTION_INPUT_FORMAT)
+        )
+            .isTrue()
+        Truth.assertThat(
+            attachedSurfaceInfo!!.implementationOptions!!
+                .retrieveOption(ImageInputConfig.OPTION_INPUT_FORMAT)
+        )
+            .isEqualTo(inputFormat)
+    }
+
+    @Test
     fun canGetTargetFrameRate() {
         Truth.assertThat(attachedSurfaceInfo!!.targetFrameRate).isEqualTo(targetFramerate)
     }
@@ -85,6 +121,8 @@
             imageFormat,
             size,
             dynamicRange,
+            listOf(CaptureType.PREVIEW),
+            config,
             null
         )
         Truth.assertThat(attachedSurfaceInfo2.targetFrameRate).isNull()
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
index 571a6b7..969f695 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import android.os.Looper.getMainLooper
 import android.util.Size
+import android.view.Surface
 import androidx.camera.core.CameraEffect
 import androidx.camera.core.CameraEffect.IMAGE_CAPTURE
 import androidx.camera.core.CameraEffect.PREVIEW
@@ -102,8 +103,10 @@
     @Test
     fun childTakingPicture_getJpegQuality() {
         // Arrange: set up StreamSharing with min latency ImageCapture as child
-        val imageCapture =
-            ImageCapture.Builder().setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY).build()
+        val imageCapture = ImageCapture.Builder()
+            .setTargetRotation(Surface.ROTATION_90)
+            .setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY)
+            .build()
         streamSharing = StreamSharing(camera, setOf(child1, imageCapture), useCaseConfigFactory)
         streamSharing.bindToCamera(camera, null, defaultConfig)
         streamSharing.onSuggestedStreamSpecUpdated(StreamSpec.builder(size).build())
@@ -116,6 +119,7 @@
 
         // Assert: the jpeg quality of min latency capture is 95.
         assertThat(sharingProcessor.jpegQuality).isEqualTo(95)
+        assertThat(sharingProcessor.rotationDegrees).isEqualTo(270)
     }
 
     @Test
@@ -297,7 +301,7 @@
         val config = streamSharing.getDefaultConfig(true, useCaseConfigFactory)!!
 
         assertThat(useCaseConfigFactory.lastRequestedCaptureType)
-            .isEqualTo(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE)
+            .isEqualTo(UseCaseConfigFactory.CaptureType.STREAM_SHARING)
         assertThat(
             config.retrieveOption(
                 OPTION_TARGET_CLASS,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
index 905f660..e6062de 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
@@ -97,7 +97,7 @@
     fun setUp() {
         virtualCamera = VirtualCamera(
             parentCamera, setOf(child1, child2), useCaseConfigFactory
-        ) {
+        ) { _, _ ->
             snapshotTriggered = true
             Futures.immediateFuture(null)
         }
@@ -255,7 +255,7 @@
         val imageCapture = ImageCapture.Builder().build()
         virtualCamera = VirtualCamera(
             parentCamera, setOf(preview, child2, imageCapture), useCaseConfigFactory
-        ) {
+        ) { _, _ ->
             Futures.immediateFuture(null)
         }
 
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index b0774f2..177e3db 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-
-import androidx.build.LibraryType
 import androidx.build.Publish
+import androidx.build.RunApiTasks
 
 plugins {
     id("AndroidXPlugin")
@@ -93,8 +92,8 @@
 
 androidx {
     name = "Jetpack Camera Testing Library"
-    type = LibraryType.INTERNAL_TEST_LIBRARY
-    publish = Publish.SNAPSHOT_ONLY
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    runApiTasks = new RunApiTasks.No("Internal testing library without any release plan yet.")
     inceptionYear = "2019"
     description = "Testing components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +"" +
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java
index b04224a..1054b0a 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java
@@ -26,6 +26,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Build;
@@ -129,6 +130,17 @@
     }
 
     /**
+     * Rotates the bitmap clockwise by the given degrees.
+     */
+    @NonNull
+    public static Bitmap rotateBitmap(@NonNull Bitmap bitmap, int rotationDegrees) {
+        Matrix matrix = new Matrix();
+        matrix.postRotate(rotationDegrees);
+        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix,
+                true);
+    }
+
+    /**
      * Calculates the average color difference between the 2 JPEG images.
      */
     public static int getAverageDiff(@NonNull byte[] jpeg1, @NonNull byte[] jpeg2) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java
index 94bd4cc..243d6ee 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java
@@ -37,6 +37,7 @@
 
     private boolean mIsReleased;
     private int mJpegQuality = 0;
+    private int mRotationDegrees = 0;
 
     /**
      * {@inheritDoc}
@@ -65,8 +66,11 @@
 
     @Override
     @NonNull
-    public ListenableFuture<Void> snapshot(int jpegQuality) {
+    public ListenableFuture<Void> snapshot(
+            @IntRange(from = 0, to = 100) int jpegQuality,
+            @IntRange(from = 0, to = 359) int rotationDegrees) {
         mJpegQuality = jpegQuality;
+        mRotationDegrees = rotationDegrees;
         return Futures.immediateFuture(null);
     }
 
@@ -74,4 +78,9 @@
     public int getJpegQuality() {
         return mJpegQuality;
     }
+
+    @IntRange(from = 0, to = 359)
+    public int getRotationDegrees() {
+        return mRotationDegrees;
+    }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java
index 6b5abe0..2e1ec2f 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java
@@ -85,6 +85,7 @@
     @Override
     public UseCaseConfig.Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) {
         return new FakeUseCaseConfig.Builder(config)
+                .setCaptureType(mCaptureType)
                 .setSessionOptionUnpacker((resolution, useCaseConfig, sessionConfigBuilder) -> {
                 });
     }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
index 88ae896..12a104a 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
@@ -274,5 +274,12 @@
             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
             return this;
         }
+
+        @NonNull
+        @Override
+        public Builder setCaptureType(@NonNull CaptureType captureType) {
+            getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
index aafc3572..b5ba2a4 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
@@ -38,6 +38,7 @@
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -105,6 +106,8 @@
                         YUV_420_888,
                         new Size(1, 1),
                         DynamicRange.SDR,
+                        singletonList(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS),
+                        preview,
                         new Range<>(30, 30));
         mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                 CameraMode.DEFAULT,
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
index 892b739..5a9196e 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
@@ -27,7 +27,11 @@
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraXConfig
 import androidx.camera.core.DynamicRange
+import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.FORMAT_HLG
+import androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT
 import androidx.camera.core.SurfaceRequest
+import androidx.camera.core.impl.CameraInfoInternal
 import androidx.camera.core.impl.MutableStateObservable
 import androidx.camera.core.impl.Observable
 import androidx.camera.core.internal.CameraUseCaseAdapter
@@ -127,7 +131,7 @@
     )
 
     private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
-    private lateinit var cameraInfo: CameraInfo
+    private lateinit var cameraInfo: CameraInfoInternal
 
     @Before
     fun setUp() {
@@ -137,7 +141,7 @@
         ).get()
 
         cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
-        cameraInfo = cameraUseCaseAdapter.cameraInfo
+        cameraInfo = cameraUseCaseAdapter.cameraInfo as CameraInfoInternal
     }
 
     @After
@@ -376,6 +380,70 @@
         } ?: fail("Timed out waiting for INACTIVE state. Waited $timeout.")
     }
 
+    @Test
+    fun defaultDynamicRange_isSdr(): Unit = runBlocking {
+        testDynamicRangeSelection { selectedDynamicRange ->
+            assertThat(selectedDynamicRange).isEqualTo(DynamicRange.SDR)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = 33) // HLG10 only supported on API 33+
+    @Test
+    fun dynamicRangeHlg_selectsHlg(): Unit = runBlocking {
+        val hlg10DynamicRange = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+        assumeTrue(
+            "Device does not support HLG10",
+            cameraInfo.supportedDynamicRanges.contains(hlg10DynamicRange)
+        )
+
+        testDynamicRangeSelection(
+            requestedDynamicRange = hlg10DynamicRange
+        ) { selectedDynamicRange ->
+            assertThat(selectedDynamicRange).isEqualTo(hlg10DynamicRange)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = 33) // 10-bit HDR only supported on API 33+
+    @Test
+    fun dynamicRangeHdrUnspecified10Bit_selectsHdr10Bit(): Unit = runBlocking {
+        val supported10BitDynamicRanges = cameraInfo.supportedDynamicRanges.filter {
+            it.bitDepth == BIT_DEPTH_10_BIT
+        }
+        assumeFalse(
+            "Device does not support any 10-bit dynamic ranges",
+            supported10BitDynamicRanges.isEmpty()
+        )
+
+        testDynamicRangeSelection(
+            requestedDynamicRange = HDR_UNSPECIFIED_10_BIT
+        ) { selectedDynamicRange ->
+            assertThat(selectedDynamicRange).isIn(supported10BitDynamicRanges)
+        }
+    }
+
+    private suspend fun testDynamicRangeSelection(
+        requestedDynamicRange: DynamicRange? = null,
+        assertBlock: (selectedDynamicRange: DynamicRange) -> Unit
+    ) {
+        // TODO(b/275632219): Disabled on camera-pipe until automatic dynamic range
+        //  selection is supported
+        assumeTrue(implName != CameraPipeConfig::class.simpleName)
+        // Arrange.
+        val videoOutput = createTestVideoOutput()
+        val videoCapture = VideoCapture.Builder(videoOutput).apply {
+            requestedDynamicRange?.let { setDynamicRange(requestedDynamicRange) }
+        }.build()
+
+        // Act.
+        withContext(Dispatchers.Main) {
+            cameraUseCaseAdapter.addUseCases(listOf(videoCapture))
+        }
+
+        // Assert.
+        val surfaceRequest = videoOutput.nextSurfaceRequest(5, TimeUnit.SECONDS)
+        assertBlock(surfaceRequest.dynamicRange)
+    }
+
     private fun createTestVideoOutput(
         streamInfo: StreamInfo = StreamInfo.of(
             StreamInfo.STREAM_ID_ANY,
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java b/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
index 0c30441..ef2fd4c 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
@@ -16,11 +16,12 @@
 
 package androidx.camera.video;
 
+import static androidx.camera.core.DynamicRange.BIT_DEPTH_UNSPECIFIED;
+import static androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED;
 import static androidx.camera.core.DynamicRange.FORMAT_HLG;
-import static androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT;
-import static androidx.camera.core.DynamicRange.SDR;
+import static androidx.camera.core.DynamicRange.FORMAT_SDR;
+import static androidx.camera.core.DynamicRange.FORMAT_UNSPECIFIED;
 import static androidx.camera.video.internal.BackupHdrProfileEncoderProfilesProvider.DEFAULT_VALIDATOR;
-import static androidx.camera.video.internal.utils.DynamicRangeUtil.VP_TO_DR_FORMAT_MAP;
 
 import android.util.Size;
 
@@ -51,7 +52,6 @@
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -74,7 +74,17 @@
 
     private static final String TAG = "RecorderVideoCapabilities";
 
-    private final Map<DynamicRange, CapabilitiesByQuality> mCapabilitiesMap = new HashMap<>();
+    private final EncoderProfilesProvider mProfilesProvider;
+
+    // Mappings of DynamicRange to recording capability information. The mappings are divided
+    // into two collections based on the key's (DynamicRange) category, one for specified
+    // DynamicRange and one for others. Specified DynamicRange means that its bit depth and
+    // format are specified values, not some wildcards, such as: FORMAT_UNSPECIFIED,
+    // FORMAT_HDR_UNSPECIFIED or BIT_DEPTH_UNSPECIFIED.
+    private final Map<DynamicRange, CapabilitiesByQuality>
+            mCapabilitiesMapForFullySpecifiedDynamicRange = new HashMap<>();
+    private final Map<DynamicRange, CapabilitiesByQuality>
+            mCapabilitiesMapForNonFullySpecifiedDynamicRange = new HashMap<>();
 
     /**
      * Creates a RecorderVideoCapabilities.
@@ -106,23 +116,18 @@
         Quirks deviceQuirks = DeviceQuirks.getAll();
         encoderProfilesProvider = new QualityValidatedEncoderProfilesProvider(
                 encoderProfilesProvider, cameraInfoInternal, deviceQuirks);
+        mProfilesProvider = encoderProfilesProvider;
 
         // Group by dynamic range.
-        for (DynamicRange dynamicRange : getCandidateDynamicRanges(encoderProfilesProvider)) {
-            if (!isDynamicRangeSupported(dynamicRange, cameraInfoInternal)) {
-                continue;
-            }
-
+        for (DynamicRange dynamicRange : cameraInfoInternal.getSupportedDynamicRanges()) {
             // Filter video profiles to include only the profiles match with the target dynamic
             // range.
             EncoderProfilesProvider constrainedProvider =
-                    new DynamicRangeMatchedEncoderProfilesProvider(encoderProfilesProvider,
-                            dynamicRange);
-            CapabilitiesByQuality capabilitiesByQuality =
-                    new CapabilitiesByQuality(constrainedProvider);
+                    new DynamicRangeMatchedEncoderProfilesProvider(mProfilesProvider, dynamicRange);
+            CapabilitiesByQuality capabilities = new CapabilitiesByQuality(constrainedProvider);
 
-            if (!capabilitiesByQuality.getSupportedQualities().isEmpty()) {
-                mCapabilitiesMap.put(dynamicRange, capabilitiesByQuality);
+            if (!capabilities.getSupportedQualities().isEmpty()) {
+                mCapabilitiesMapForFullySpecifiedDynamicRange.put(dynamicRange, capabilities);
             }
         }
     }
@@ -144,25 +149,20 @@
     @NonNull
     @Override
     public Set<DynamicRange> getSupportedDynamicRanges() {
-        Set<DynamicRange> dynamicRanges = mCapabilitiesMap.keySet();
-
-        // Remove HDR_UNSPECIFIED_10_BIT from output, since it does not have explicit content.
-        dynamicRanges.remove(HDR_UNSPECIFIED_10_BIT);
-
-        return dynamicRanges;
+        return mCapabilitiesMapForFullySpecifiedDynamicRange.keySet();
     }
 
     @NonNull
     @Override
     public List<Quality> getSupportedQualities(@NonNull DynamicRange dynamicRange) {
-        CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+        CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
         return capabilities == null ? new ArrayList<>() : capabilities.getSupportedQualities();
     }
 
     @Override
     public boolean isQualitySupported(@NonNull Quality quality,
             @NonNull DynamicRange dynamicRange) {
-        CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+        CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
         return capabilities != null && capabilities.isQualitySupported(quality);
     }
 
@@ -170,7 +170,7 @@
     @Override
     public VideoValidatedEncoderProfilesProxy getProfiles(@NonNull Quality quality,
             @NonNull DynamicRange dynamicRange) {
-        CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+        CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
         return capabilities == null ? null : capabilities.getProfiles(quality);
     }
 
@@ -178,7 +178,7 @@
     @Override
     public VideoValidatedEncoderProfilesProxy findHighestSupportedEncoderProfilesFor(
             @NonNull Size size, @NonNull DynamicRange dynamicRange) {
-        CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+        CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
         return capabilities == null ? null : capabilities.findHighestSupportedEncoderProfilesFor(
                 size);
     }
@@ -187,57 +187,28 @@
     @Override
     public Quality findHighestSupportedQualityFor(@NonNull Size size,
             @NonNull DynamicRange dynamicRange) {
-        CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+        CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
         return capabilities == null ? Quality.NONE : capabilities.findHighestSupportedQualityFor(
                 size);
     }
 
-    @NonNull
-    private static Set<DynamicRange> getCandidateDynamicRanges(
-            @NonNull EncoderProfilesProvider provider) {
-        Set<DynamicRange> dynamicRanges = new HashSet<>();
-        for (Quality quality : Quality.getSortedQualities()) {
-            int qualityValue = ((Quality.ConstantQuality) quality).getValue();
-            EncoderProfilesProxy encoderProfiles = provider.getAll(qualityValue);
-
-            if (encoderProfiles != null) {
-                for (VideoProfileProxy videoProfile : encoderProfiles.getVideoProfiles()) {
-                    Integer format = VP_TO_DR_FORMAT_MAP.get(videoProfile.getHdrFormat());
-                    if (format != null) {
-                        dynamicRanges.add(new DynamicRange(format, videoProfile.getBitDepth()));
-                    }
-                }
-            }
+    @Nullable
+    private CapabilitiesByQuality getCapabilities(@NonNull DynamicRange dynamicRange) {
+        if (isFullySpecified(dynamicRange)) {
+            return mCapabilitiesMapForFullySpecifiedDynamicRange.get(dynamicRange);
         }
 
-        dynamicRanges.add(SDR);
-        dynamicRanges.add(HDR_UNSPECIFIED_10_BIT);
-
-        return dynamicRanges;
-    }
-
-    private static boolean isDynamicRangeSupported(@NonNull DynamicRange dynamicRange,
-            @NonNull CameraInfoInternal cameraInfoInternal) {
-        Set<DynamicRange> supportedDynamicRanges = cameraInfoInternal.getSupportedDynamicRanges();
-        if (supportedDynamicRanges.contains(dynamicRange)) {
-            return true;
+        // Handle dynamic range that is not fully specified.
+        if (mCapabilitiesMapForNonFullySpecifiedDynamicRange.containsKey(dynamicRange)) {
+            return mCapabilitiesMapForNonFullySpecifiedDynamicRange.get(dynamicRange);
         } else {
-            return dynamicRange.equals(HDR_UNSPECIFIED_10_BIT) && contains10BitHdrDynamicRange(
-                    supportedDynamicRanges);
+            CapabilitiesByQuality capabilities =
+                    generateCapabilitiesForNonFullySpecifiedDynamicRange(dynamicRange);
+            mCapabilitiesMapForNonFullySpecifiedDynamicRange.put(dynamicRange, capabilities);
+            return capabilities;
         }
     }
 
-    private static boolean contains10BitHdrDynamicRange(@NonNull Set<DynamicRange> dynamicRanges) {
-        for (DynamicRange dynamicRange : dynamicRanges) {
-            if (dynamicRange.getFormat() != DynamicRange.FORMAT_SDR
-                    && dynamicRange.getBitDepth() == DynamicRange.BIT_DEPTH_10_BIT) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     private static boolean isHlg10SupportedByCamera(
             @NonNull CameraInfoInternal cameraInfoInternal) {
         Set<DynamicRange> dynamicRanges = cameraInfoInternal.getSupportedDynamicRanges();
@@ -252,6 +223,78 @@
         return false;
     }
 
+    @Nullable
+    private CapabilitiesByQuality generateCapabilitiesForNonFullySpecifiedDynamicRange(
+            @NonNull DynamicRange dynamicRange) {
+        if (!canResolve(dynamicRange, getSupportedDynamicRanges())) {
+            return null;
+        }
+
+        // Filter video profiles to include only the profiles match with the target dynamic
+        // range.
+        EncoderProfilesProvider constrainedProvider =
+                new DynamicRangeMatchedEncoderProfilesProvider(mProfilesProvider, dynamicRange);
+        return new CapabilitiesByQuality(constrainedProvider);
+    }
+
+    /**
+     * Returns {@code true} if the test dynamic range can resolve to the fully specified dynamic
+     * range set.
+     *
+     * <p>A range can resolve if test fields are unspecified and appropriately match the fields
+     * of the fully specified dynamic range, or the test fields exactly match the fields of
+     * the fully specified dynamic range.
+     */
+    private static boolean canResolve(@NonNull DynamicRange dynamicRangeToTest,
+            @NonNull Set<DynamicRange> fullySpecifiedDynamicRanges) {
+        if (isFullySpecified(dynamicRangeToTest)) {
+            return fullySpecifiedDynamicRanges.contains(dynamicRangeToTest);
+        } else {
+            for (DynamicRange fullySpecifiedDynamicRange : fullySpecifiedDynamicRanges) {
+                if (canMatchBitDepth(dynamicRangeToTest, fullySpecifiedDynamicRange)
+                        && canMatchFormat(dynamicRangeToTest, fullySpecifiedDynamicRange)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    private static boolean canMatchBitDepth(@NonNull DynamicRange dynamicRangeToTest,
+            @NonNull DynamicRange fullySpecifiedDynamicRange) {
+        Preconditions.checkState(isFullySpecified(fullySpecifiedDynamicRange), "Fully specified "
+                + "range is not actually fully specified.");
+        if (dynamicRangeToTest.getBitDepth() == BIT_DEPTH_UNSPECIFIED) {
+            return true;
+        }
+
+        return dynamicRangeToTest.getBitDepth() == fullySpecifiedDynamicRange.getBitDepth();
+    }
+
+    private static boolean canMatchFormat(@NonNull DynamicRange dynamicRangeToTest,
+            @NonNull DynamicRange fullySpecifiedDynamicRange) {
+        Preconditions.checkState(isFullySpecified(fullySpecifiedDynamicRange), "Fully specified "
+                + "range is not actually fully specified.");
+        int formatToTest = dynamicRangeToTest.getFormat();
+        if (formatToTest == FORMAT_UNSPECIFIED) {
+            return true;
+        }
+
+        int fullySpecifiedFormat = fullySpecifiedDynamicRange.getFormat();
+        if (formatToTest == FORMAT_HDR_UNSPECIFIED && fullySpecifiedFormat != FORMAT_SDR) {
+            return true;
+        }
+
+        return formatToTest == fullySpecifiedFormat;
+    }
+
+    private static boolean isFullySpecified(@NonNull DynamicRange dynamicRange) {
+        return dynamicRange.getFormat() != FORMAT_UNSPECIFIED
+                && dynamicRange.getFormat() != FORMAT_HDR_UNSPECIFIED
+                && dynamicRange.getBitDepth() != BIT_DEPTH_UNSPECIFIED;
+    }
+
     /**
      * This class implements the video capabilities query logic related to quality and resolution.
      */
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 8c83205..8d75923 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
@@ -17,7 +17,6 @@
 package androidx.camera.video;
 
 import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE;
-import static androidx.camera.core.DynamicRange.SDR;
 import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
@@ -29,6 +28,7 @@
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_SESSION_CONFIG;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
@@ -91,6 +91,7 @@
 import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.ConfigProvider;
 import androidx.camera.core.impl.DeferrableSurface;
+import androidx.camera.core.impl.ImageInputConfig;
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
 import androidx.camera.core.impl.MutableConfig;
@@ -219,7 +220,8 @@
      */
     @NonNull
     public static <T extends VideoOutput> VideoCapture<T> withOutput(@NonNull T videoOutput) {
-        return new VideoCapture.Builder<>(Preconditions.checkNotNull(videoOutput)).build();
+        return new VideoCapture.Builder<>(Preconditions.checkNotNull(videoOutput)).setCaptureType(
+                UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE).build();
     }
 
     /**
@@ -424,6 +426,31 @@
     }
 
     /**
+     * Returns the dynamic range.
+     *
+     * <p>The dynamic range is set by {@link VideoCapture.Builder#setDynamicRange(DynamicRange)}.
+     * If the dynamic range set is not a fully defined dynamic range, such as
+     * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}, then it will be returned just as provided,
+     * and will not be returned as a fully defined dynamic range.
+     *
+     * <p>If the dynamic range was not provided to
+     * {@link VideoCapture.Builder#setDynamicRange(DynamicRange)}, this will return the default of
+     * {@link DynamicRange#SDR}
+     *
+     * @return the dynamic range set for this {@code VideoCapture} use case.
+     */
+    // Internal implementation note: this method should not be used to retrieve the dynamic range
+    // that will be sent to the VideoOutput. That should always be retrieved from the StreamSpec
+    // since that will be the final DynamicRange chosen by the camera based on other use case
+    // combinations.
+    @RestrictTo(Scope.LIBRARY)
+    @NonNull
+    public DynamicRange getDynamicRange() {
+        return getCurrentConfig().hasDynamicRange() ? getCurrentConfig().getDynamicRange() :
+                Defaults.DEFAULT_DYNAMIC_RANGE;
+    }
+
+    /**
      * {@inheritDoc}
      *
      */
@@ -497,7 +524,7 @@
     public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
             @NonNull UseCaseConfigFactory factory) {
         Config captureConfig = factory.getConfig(
-                UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE,
+                DEFAULT_CONFIG.getConfig().getCaptureType(),
                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
 
         if (applyDefaultConfig) {
@@ -770,10 +797,18 @@
 
         static final Range<Integer> DEFAULT_FPS_RANGE = new Range<>(30, 30);
 
+        /**
+         * Explicitly setting the default dynamic range to SDR (rather than UNSPECIFIED) means
+         * VideoCapture won't inherit dynamic ranges from other use cases.
+         */
+        static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR;
+
         static {
             Builder<?> builder = new Builder<>(DEFAULT_VIDEO_OUTPUT)
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
-                    .setVideoEncoderInfoFinder(DEFAULT_VIDEO_ENCODER_INFO_FINDER);
+                    .setVideoEncoderInfoFinder(DEFAULT_VIDEO_ENCODER_INFO_FINDER)
+                    .setDynamicRange(DEFAULT_DYNAMIC_RANGE)
+                    .setCaptureType(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -1209,12 +1244,12 @@
         Preconditions.checkArgument(mediaSpec != null,
                 "Unable to update target resolution by null MediaSpec.");
 
-        DynamicRange dynamicRange = Preconditions.checkNotNull(
-                builder.getMutableConfig().retrieveOption(OPTION_INPUT_DYNAMIC_RANGE, SDR));
+        DynamicRange requestedDynamicRange = getDynamicRange();
         VideoCapabilities videoCapabilities = getVideoCapabilities(cameraInfo);
 
         // Get supported qualities.
-        List<Quality> supportedQualities = videoCapabilities.getSupportedQualities(dynamicRange);
+        List<Quality> supportedQualities = videoCapabilities.getSupportedQualities(
+                requestedDynamicRange);
         if (supportedQualities.isEmpty()) {
             // When the device does not have any supported quality, even the most flexible
             // QualitySelector such as QualitySelector.from(Quality.HIGHEST), still cannot
@@ -1238,7 +1273,8 @@
 
         // Get corresponded resolutions for the target aspect ratio.
         int aspectRatio = videoSpec.getAspectRatio();
-        Map<Quality, Size> sizeMap = getQualityToResolutionMap(videoCapabilities, dynamicRange);
+        Map<Quality, Size> sizeMap = getQualityToResolutionMap(videoCapabilities,
+                requestedDynamicRange);
         QualityRatioToResolutionsTable qualityRatioTable = new QualityRatioToResolutionsTable(
                 cameraInfo.getSupportedResolutions(getImageFormat()), sizeMap);
         List<Size> customOrderedResolutions = new ArrayList<>();
@@ -1325,7 +1361,8 @@
     @SuppressWarnings("ObjectToString")
     public static final class Builder<T extends VideoOutput> implements
             UseCaseConfig.Builder<VideoCapture<T>, VideoCaptureConfig<T>, Builder<T>>,
-            ImageOutputConfig.Builder<Builder<T>>, ThreadConfig.Builder<Builder<T>> {
+            ImageOutputConfig.Builder<Builder<T>>, ImageInputConfig.Builder<Builder<T>>,
+            ThreadConfig.Builder<Builder<T>> {
         private final MutableOptionsBundle mMutableConfig;
 
         /** Creates a new Builder object. */
@@ -1590,6 +1627,41 @@
             return this;
         }
 
+        // Implementations of ImageInputConfig.Builder default methods
+
+        /**
+         * Sets the {@link DynamicRange}.
+         *
+         * <p>The dynamic range specifies how the range of colors, highlights and shadows that
+         * are captured by the video producer are displayed on a display. Some dynamic ranges will
+         * allow the video to make full use of the extended range of brightness of a display when
+         * the video is played back.
+         *
+         * <p>The supported dynamic ranges for video capture depend on the capabilities of the
+         * camera and the {@link VideoOutput}. The supported dynamic ranges can normally be
+         * queried through the specific video output. For example, the available dynamic
+         * ranges for the {@link Recorder} video output can be queried through
+         * the {@link androidx.camera.video.VideoCapabilities} returned by
+         * {@link Recorder#getVideoCapabilities(CameraInfo)} via
+         * {@link androidx.camera.video.VideoCapabilities#getSupportedDynamicRanges()}.
+         *
+         * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by
+         * providing {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}.
+         *
+         * <p>If the dynamic range is not provided, the returned video capture use case will use
+         * a default of {@link DynamicRange#SDR}.
+         *
+         * @return The current Builder.
+         * @see DynamicRange
+         */
+        @RestrictTo(Scope.LIBRARY)
+        @NonNull
+        @Override
+        public Builder<T> setDynamicRange(@NonNull DynamicRange dynamicRange) {
+            getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange);
+            return this;
+        }
+
         // Implementations of ThreadConfig.Builder default methods
 
         /**
@@ -1703,5 +1775,13 @@
             getMutableConfig().insertOption(OPTION_TARGET_FRAME_RATE, targetFrameRate);
             return this;
         }
+
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        @Override
+        public Builder<T> setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+            getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
index 8057fdf..e079aba 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
@@ -31,6 +31,7 @@
 import static androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED;
 import static androidx.camera.core.DynamicRange.FORMAT_HLG;
 import static androidx.camera.core.DynamicRange.FORMAT_SDR;
+import static androidx.camera.core.DynamicRange.FORMAT_UNSPECIFIED;
 import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_10;
 import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8;
 
@@ -65,6 +66,8 @@
                 new HashSet<>(asList(BIT_DEPTH_8, BIT_DEPTH_10)));
 
         // DynamicRange format to VideoProfile HDR format.
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_UNSPECIFIED, new HashSet<>(asList(HDR_NONE, HDR_HLG,
+                HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)));
         DR_TO_VP_FORMAT_MAP.put(FORMAT_SDR, new HashSet<>(singletonList(HDR_NONE)));
         DR_TO_VP_FORMAT_MAP.put(FORMAT_HDR_UNSPECIFIED,
                 new HashSet<>(asList(HDR_HLG, HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)));
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt
index fb598f9..f917aca 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt
@@ -21,9 +21,16 @@
 import android.util.Size
 import androidx.camera.core.DynamicRange
 import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.BIT_DEPTH_8_BIT
+import androidx.camera.core.DynamicRange.BIT_DEPTH_UNSPECIFIED
+import androidx.camera.core.DynamicRange.FORMAT_DOLBY_VISION
+import androidx.camera.core.DynamicRange.FORMAT_HDR10
+import androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED
 import androidx.camera.core.DynamicRange.FORMAT_HLG
+import androidx.camera.core.DynamicRange.FORMAT_UNSPECIFIED
 import androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT
 import androidx.camera.core.DynamicRange.SDR
+import androidx.camera.core.DynamicRange.UNSPECIFIED
 import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
 import androidx.camera.testing.EncoderProfilesUtil.PROFILES_2160P
 import androidx.camera.testing.EncoderProfilesUtil.PROFILES_720P
@@ -48,6 +55,11 @@
 import org.robolectric.annotation.internal.DoNotInstrument
 
 private val HLG10 = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+private val HDR10 = DynamicRange(FORMAT_HDR10, BIT_DEPTH_10_BIT)
+private val UNSPECIFIED_8_BIT = DynamicRange(FORMAT_UNSPECIFIED, BIT_DEPTH_8_BIT)
+private val UNSPECIFIED_10_BIT = DynamicRange(FORMAT_UNSPECIFIED, BIT_DEPTH_10_BIT)
+private val HDR_UNSPECIFIED = DynamicRange(FORMAT_HDR_UNSPECIFIED, BIT_DEPTH_UNSPECIFIED)
+private val DOLBY_VISION_UNSPECIFIED = DynamicRange(FORMAT_DOLBY_VISION, BIT_DEPTH_UNSPECIFIED)
 
 @RunWith(RobolectricTestRunner::class)
 @DoNotInstrument
@@ -80,6 +92,55 @@
     }
 
     @Test
+    fun hasSupportedQualities_sdr() {
+        assertThat(videoCapabilities.getSupportedQualities(SDR)).containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_hlg10() {
+        assertThat(videoCapabilities.getSupportedQualities(HLG10)).containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_hdr10() {
+        assertThat(videoCapabilities.getSupportedQualities(HDR10)).isEmpty()
+    }
+
+    @Test
+    fun hasSupportedQualities_unspecified() {
+        assertThat(videoCapabilities.getSupportedQualities(UNSPECIFIED)).containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_hdrUnspecified() {
+        assertThat(videoCapabilities.getSupportedQualities(HDR_UNSPECIFIED))
+            .containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_hdrUnspecified10Bit() {
+        assertThat(videoCapabilities.getSupportedQualities(HDR_UNSPECIFIED_10_BIT))
+            .containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_unspecified8Bit() {
+        assertThat(videoCapabilities.getSupportedQualities(UNSPECIFIED_8_BIT))
+            .containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_unspecified10Bit() {
+        assertThat(videoCapabilities.getSupportedQualities(UNSPECIFIED_10_BIT))
+            .containsExactly(HD, UHD)
+    }
+
+    @Test
+    fun hasSupportedQualities_dolbyVisionUnspecified() {
+        assertThat(videoCapabilities.getSupportedQualities(DOLBY_VISION_UNSPECIFIED)).isEmpty()
+    }
+
+    @Test
     fun isQualitySupported_sdr() {
         assertThat(videoCapabilities.isQualitySupported(HIGHEST, SDR)).isTrue()
         assertThat(videoCapabilities.isQualitySupported(LOWEST, SDR)).isTrue()
@@ -90,6 +151,16 @@
     }
 
     @Test
+    fun isQualitySupported_unspecified() {
+        assertThat(videoCapabilities.isQualitySupported(HIGHEST, UNSPECIFIED)).isTrue()
+        assertThat(videoCapabilities.isQualitySupported(LOWEST, UNSPECIFIED)).isTrue()
+        assertThat(videoCapabilities.isQualitySupported(UHD, UNSPECIFIED)).isTrue()
+        assertThat(videoCapabilities.isQualitySupported(FHD, UNSPECIFIED)).isFalse()
+        assertThat(videoCapabilities.isQualitySupported(HD, UNSPECIFIED)).isTrue()
+        assertThat(videoCapabilities.isQualitySupported(SD, UNSPECIFIED)).isFalse()
+    }
+
+    @Test
     fun isQualitySupported_hlg10WithBackupProfile() {
         assertThat(videoCapabilities.isQualitySupported(HIGHEST, HLG10)).isTrue()
         assertThat(videoCapabilities.isQualitySupported(LOWEST, HLG10)).isTrue()
@@ -100,7 +171,7 @@
     }
 
     @Test
-    fun isQualitySupported_hdrUnspecifiedWithBackupProfile() {
+    fun isQualitySupported_hdrUnspecified10BitWithBackupProfile() {
         assertThat(videoCapabilities.isQualitySupported(HIGHEST, HDR_UNSPECIFIED_10_BIT)).isTrue()
         assertThat(videoCapabilities.isQualitySupported(LOWEST, HDR_UNSPECIFIED_10_BIT)).isTrue()
         assertThat(videoCapabilities.isQualitySupported(UHD, HDR_UNSPECIFIED_10_BIT)).isTrue()
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
index 93f2245..02ef374 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
@@ -45,6 +45,8 @@
 import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
 import androidx.camera.core.CameraXConfig
 import androidx.camera.core.DynamicRange
+import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.FORMAT_HLG
 import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
@@ -747,6 +749,20 @@
     }
 
     @Test
+    fun defaultDynamicRangeIsSdr() {
+        val videoCapture = createVideoCapture()
+        assertThat(videoCapture.dynamicRange).isEqualTo(DynamicRange.SDR)
+    }
+
+    @Test
+    fun canSetDynamicRange() {
+        val videoCapture = createVideoCapture(
+            dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT
+        )
+        assertThat(videoCapture.dynamicRange).isEqualTo(DynamicRange.HDR_UNSPECIFIED_10_BIT)
+    }
+
+    @Test
     fun defaultMirrorModeIsOff() {
         val videoCapture = createVideoCapture()
         assertThat(videoCapture.mirrorMode).isEqualTo(MIRROR_MODE_OFF)
@@ -1017,6 +1033,16 @@
         )
     }
 
+    @Test
+    fun suggestedStreamSpecDynamicRange_isPropagatedToSurfaceRequest() {
+        // This ensures the dynamic range set on the VideoCapture.Builder is not just directly
+        // propagated to the SurfaceRequest. It should come from the StreamSpec.
+        testSurfaceRequestContainsExpected(
+            requestedDynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT,
+            expectedDynamicRange = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+        )
+    }
+
     private fun testSetTargetRotation_transformationInfoUpdated(
         lensFacing: Int = LENS_FACING_BACK,
         sensorRotationDegrees: Int = 0,
@@ -1256,14 +1282,17 @@
         cropRect: Rect? = null,
         expectedCropRect: Rect? = null,
         targetFrameRate: Range<Int>? = null,
-        expectedFrameRate: Range<Int> = SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
+        expectedFrameRate: Range<Int> = SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED,
+        requestedDynamicRange: DynamicRange? = null,
+        expectedDynamicRange: DynamicRange? = null
     ) {
         // Arrange.
         setupCamera()
         createCameraUseCaseAdapter()
         setSuggestedStreamSpec(
             quality,
-            expectedFrameRate = expectedFrameRate
+            expectedFrameRate = expectedFrameRate,
+            dynamicRange = expectedDynamicRange
         )
         var surfaceRequest: SurfaceRequest? = null
         val videoOutput = createVideoOutput(
@@ -1275,7 +1304,8 @@
         val videoCapture = createVideoCapture(
             videoOutput,
             videoEncoderInfoFinder = { videoEncoderInfo },
-            targetFrameRate = targetFrameRate
+            targetFrameRate = targetFrameRate,
+            dynamicRange = requestedDynamicRange
         )
 
         cropRect?.let {
@@ -1296,6 +1326,10 @@
         if (expectedFrameRate != StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED) {
             assertThat(surfaceRequest!!.expectedFrameRate).isEqualTo(expectedFrameRate)
         }
+
+        expectedDynamicRange?.let {
+            assertThat(surfaceRequest!!.dynamicRange).isEqualTo(expectedDynamicRange)
+        }
     }
 
     private fun assertCustomOrderedResolutions(
@@ -1393,6 +1427,7 @@
         mirrorMode: Int? = null,
         targetResolution: Size? = null,
         targetFrameRate: Range<Int>? = null,
+        dynamicRange: DynamicRange? = null,
         videoEncoderInfoFinder: Function<VideoEncoderConfig, VideoEncoderInfo> =
             Function { createVideoEncoderInfo() },
     ): VideoCapture<VideoOutput> = VideoCapture.Builder(videoOutput)
@@ -1402,6 +1437,7 @@
             mirrorMode?.let { setMirrorMode(it) }
             targetResolution?.let { setTargetResolution(it) }
             targetFrameRate?.let { setTargetFrameRate(it) }
+            dynamicRange?.let { setDynamicRange(it) }
             setVideoEncoderInfoFinder(videoEncoderInfoFinder)
         }.build()
 
@@ -1428,11 +1464,14 @@
 
     private fun setSuggestedStreamSpec(
         quality: Quality,
-        expectedFrameRate: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED
+        expectedFrameRate: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED,
+        dynamicRange: DynamicRange? = null
     ) {
         setSuggestedStreamSpec(
-            StreamSpec.builder(CAMERA_0_QUALITY_SIZE[quality]!!)
-                .setExpectedFrameRateRange(expectedFrameRate).build()
+            StreamSpec.builder(CAMERA_0_QUALITY_SIZE[quality]!!).apply {
+                setExpectedFrameRateRange(expectedFrameRate)
+                dynamicRange?.let { setDynamicRange(dynamicRange) }
+            }.build()
         )
     }
 
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
index 0c9a82a..912d748 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
@@ -100,6 +100,9 @@
             "Ignore Cuttlefish",
             Build.MODEL.contains("Cuttlefish")
         )
+        Assume.assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+            " the test failure.",
+            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
         Assume.assumeTrue(CameraUtil.deviceHasCamera())
         CoreAppTestUtil.assumeCompatibleDevice()
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index e6dbf12..5402bfe 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -150,6 +150,9 @@
 
     @Before
     fun setUp(): Unit = runBlocking {
+        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+            " the test failure.",
+            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
         assumeTrue(CameraUtil.hasCameraWithLensFacing(BACK_LENS_FACING))
         createDefaultPictureFolderIfNotExist()
         ProcessCameraProvider.configureInstance(cameraXConfig)
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
index ffc5c97..1ff8e45 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
@@ -76,6 +76,10 @@
                 "redmi note 8"
             ).contains(Build.MODEL.lowercase(Locale.US)) && rotation == Surface.ROTATION_180
         )
+        Assume.assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+            " the test failure.",
+            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
+
         setUp(lensFacing)
     }
 
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index ab39956..b7d5960 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -115,6 +115,9 @@
 
     @Before
     fun setup() {
+        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+            " the test failure.",
+            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window before start the test.
         CoreAppTestUtil.prepareDeviceUI(instrumentation)
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
index a0e60d4..d33caaa 100644
--- a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
@@ -58,4 +58,5 @@
   android.view.inputmethod.ExtractedText getExtractedText(in android.view.inputmethod.ExtractedTextRequest request, int flags) = 22;
   void closeConnection() = 23;
   android.view.inputmethod.EditorInfo getEditorInfo() = 24;
+  androidx.car.app.serialization.Bundleable getSurroundingText(int beforeLength, int afterLength, int flags) = 25;
 }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java
index a4a1474..2220696 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java
@@ -16,21 +16,28 @@
 
 package androidx.car.app.activity.renderer.surface;
 
+import static androidx.car.app.utils.LogTags.TAG;
+
 import static java.util.Objects.requireNonNull;
 
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.SurroundingText;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.app.activity.ServiceDispatcher;
 import androidx.car.app.activity.renderer.IProxyInputConnection;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
 
 /** Proxies input connection calls to the provided {@link IProxyInputConnection}. */
 final class RemoteProxyInputConnection extends InputConnectionWrapper {
@@ -77,7 +84,7 @@
     public ExtractedText getExtractedText(@NonNull ExtractedTextRequest request, int flags) {
         requireNonNull(request);
         return mServiceDispatcher.fetch("getExtractedText", null, () ->
-                        mProxyInputConnection.getExtractedText(request, flags));
+                mProxyInputConnection.getExtractedText(request, flags));
     }
 
     @Override
@@ -213,6 +220,24 @@
 
     @Nullable
     @Override
+    public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            return mServiceDispatcher.fetch("getSurroundingText", null, () -> {
+                try {
+                    Bundleable bundleable = mProxyInputConnection.getSurroundingText(beforeLength,
+                            afterLength, flags);
+                    return bundleable == null ? null : (SurroundingText) bundleable.get();
+                } catch (BundlerException e) {
+                    Log.e(TAG, "Cannot get surrounding text", e);
+                    return null;
+                }
+            });
+        }
+        return null;
+    }
+
+    @Nullable
+    @Override
     public Handler getHandler() {
         return null;
     }
diff --git a/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl b/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
index 9bd5d7c..430cca5 100644
--- a/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
+++ b/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
@@ -23,6 +23,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
+import androidx.car.app.serialization.Bundleable;
 
 /**
  * Proxies the {@link InputConnection} method invocations from {@link CarAppActivity} across a
@@ -102,4 +103,12 @@
 
     /** Returns the {@link EditorInfo} associated with the input connection. */
     EditorInfo getEditorInfo() = 24;
-}
\ No newline at end of file
+
+    /**
+     * Proxies a call to {@link InputConnection#getSurroundingText}.
+     * Note that this returns a {@link Bundleable} that wraps a {@link SurroundingText} since the
+     * latter is only available on Android S+. Note that this returns {@code null} on Android R- or
+     * when an exception is thrown.
+     */
+    Bundleable getSurroundingText(int beforeLength, int afterLength, int flags) = 25;
+}
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnectionTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnectionTest.java
new file mode 100644
index 0000000..08d969d
--- /dev/null
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnectionTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.activity.renderer.surface;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.RemoteException;
+import android.view.inputmethod.SurroundingText;
+
+import androidx.car.app.activity.CarAppViewModel;
+import androidx.car.app.activity.ServiceDispatcher;
+import androidx.car.app.activity.renderer.IProxyInputConnection;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+/** Tests for {@link RemoteProxyInputConnection} */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class RemoteProxyInputConnectionTest {
+    private RemoteProxyInputConnection mRemoteProxyInputConnection;
+    private final CarAppViewModel mViewModel = mock(CarAppViewModel.class);
+    private final ServiceDispatcher mServiceDispatcher = new ServiceDispatcher(mViewModel,
+            () -> true);
+    private final IProxyInputConnection mProxyInputConnection =
+            mock(IProxyInputConnection.class);
+
+
+    @Before
+    public void setUp() throws Exception {
+        mRemoteProxyInputConnection =
+                new RemoteProxyInputConnection(mServiceDispatcher, mProxyInputConnection);
+
+    }
+
+    @Config(maxSdk = 30)
+    @Test
+    public void getSurroundingText_apiLevel30Minus_returnsNull() {
+        assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+    }
+
+    @Config(minSdk = 31)
+    @Test
+    public void getSurroundingText_proxyInputReturnsValidValue_returnsValidValue()
+            throws RemoteException,
+            BundlerException {
+        SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+        when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenReturn(
+                Bundleable.create(surroundingText));
+        assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isEqualTo(
+                surroundingText);
+    }
+
+    @Config(minSdk = 31)
+    @Test
+    public void getSurroundingText_proxyInputReturnsNull_returnsNull() throws RemoteException,
+            BundlerException {
+        SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+        when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenReturn(null);
+        assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+    }
+
+    @Config(minSdk = 31)
+    @Test
+    public void getSurroundingText_throwsRemoteException_returnsNull() throws RemoteException,
+            BundlerException {
+        SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+        when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenThrow(RemoteException.class);
+        assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+    }
+
+    @Config(minSdk = 31)
+    @Test
+    public void getSurroundingText_throwsBundlerException_returnsNull() throws RemoteException,
+            BundlerException {
+        SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+        when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenReturn(
+                Bundleable.create("random string"));
+        assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+    }
+}
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 97ec824..c37b862 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
@@ -94,7 +94,7 @@
     <string name="no_energy_profile_permission" msgid="4662285713731308888">"Нема дозвола за енергетски профил."</string>
     <string name="fuel_types" msgid="6811375173343218212">"Типови гориво"</string>
     <string name="unavailable" msgid="3636401138255192934">"Недостапно"</string>
-    <string name="ev_connector_types" msgid="735458637011996125">"Типови конектори за ЕВ"</string>
+    <string name="ev_connector_types" msgid="735458637011996125">"Типови конектори за EV"</string>
     <string name="example_title" msgid="530257630320010494">"Пример %d"</string>
     <string name="example_1_text" msgid="8456567953748293512">"Текстов има црвена боја"</string>
     <string name="example_2_text" msgid="718820705318661440">"Текстов има зелена боја"</string>
@@ -181,7 +181,7 @@
     <string name="no_energy_level_permission" msgid="1684773185095107825">"Нема дозвола за EnergyLevel."</string>
     <string name="no_speed_permission" msgid="5812532480922675390">"Нема дозвола за брзина."</string>
     <string name="no_mileage_permission" msgid="4074779840599589847">"Нема дозвола за километража."</string>
-    <string name="no_ev_status_permission" msgid="933075402821938973">"Нема дозвола за статусот на ЕВ."</string>
+    <string name="no_ev_status_permission" msgid="933075402821938973">"Нема дозвола за статусот на EV."</string>
     <string name="no_accelerometer_permission" msgid="896914448469117234">"Нема дозвола за акцелерометар."</string>
     <string name="no_gyroscope_permission" msgid="665293140266771569">"Нема дозвола за жироскоп."</string>
     <string name="no_compass_permission" msgid="5162304489577567125">"Нема дозвола за компас."</string>
@@ -190,7 +190,7 @@
     <string name="fetch_energy_level" msgid="1773415471137542832">"Се презема ниво на енергија."</string>
     <string name="fetch_speed" msgid="7333830984597189627">"Се презема брзина."</string>
     <string name="fetch_mileage" msgid="7490131687788025123">"Се презема километража."</string>
-    <string name="fetch_ev_status" msgid="2798910410830567052">"Се презема статус на ЕВ."</string>
+    <string name="fetch_ev_status" msgid="2798910410830567052">"Се презема статус на EV."</string>
     <string name="fetch_accelerometer" msgid="697750041126810911">"Се презема акцелерометар."</string>
     <string name="fetch_gyroscope" msgid="7153155318827188539">"Се презема жироскоп."</string>
     <string name="fetch_compass" msgid="7316188117590056717">"Се презема компас."</string>
@@ -204,8 +204,8 @@
     <string name="raw_speed" msgid="7295910214088983967">"Брзина на RAW"</string>
     <string name="unit" msgid="7697521583928135171">"Единица"</string>
     <string name="odometer" msgid="3925174645651546591">"Броило"</string>
-    <string name="ev_connected" msgid="2277845607662494696">"Поврзана е порта за полнење ЕВ"</string>
-    <string name="ev_open" msgid="4916704450914519643">"Отворена е порта за полнење ЕВ"</string>
+    <string name="ev_connected" msgid="2277845607662494696">"Поврзана е порта за полнење EV"</string>
+    <string name="ev_open" msgid="4916704450914519643">"Отворена е порта за полнење EV"</string>
     <string name="accelerometer" msgid="2084026313768299185">"Акцелерометар"</string>
     <string name="gyroscope" msgid="3428075828014504651">"Жироскоп"</string>
     <string name="compass" msgid="7037367764762441245">"Компас"</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 e83b300..b52261b 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
@@ -119,7 +119,7 @@
     <string name="loading_demo_row_title" msgid="8933049915126088142">"Ngarkimi përfundoi!"</string>
     <string name="pop_to_root" msgid="2078277386355064198">"Hidhu te rrënja"</string>
     <string name="pop_to_marker" msgid="5007078308762725207">"Hidhu te shënuesi i demonstrimeve të ndryshme"</string>
-    <string name="push_stack" msgid="2433062141810168976">"Shty më tej te stiva"</string>
+    <string name="push_stack" msgid="2433062141810168976">"Shtyj më tej te stiva"</string>
     <string name="pop_to_prefix" msgid="4288884615669751608">"Hidhu te"</string>
     <string name="pop_to_title" msgid="3924696281273379455">"Hidhu te demonstrimi"</string>
     <string name="package_not_found_error_msg" msgid="7525619456883627939">"Paketa nuk u gjet."</string>
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
index f21dbb9..c38b355 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
@@ -219,6 +219,7 @@
         ) { atEnd ->
             if (atEnd) overallDuration.toFloat() else 0f
         }
+        @Suppress("UnrememberedMutableState") // b/279909531
         return derivedStateOf { interpolate(timeState.value) }
     }
 
diff --git a/compose/compiler/OWNERS b/compose/compiler/OWNERS
index fc11d9b..e48b97d 100644
--- a/compose/compiler/OWNERS
+++ b/compose/compiler/OWNERS
@@ -2,4 +2,5 @@
 jsproch@google.com
 chuckj@google.com
 lelandr@google.com
+anbailey@google.com
 per-file settings.gradle = dustinlam@google.com, rahulrav@google.com
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index a8baf7a..46e9c83 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+
+import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
 import androidx.build.RunApiTasks
 import androidx.build.BuildOnServerKt
@@ -26,41 +29,55 @@
     id("kotlin-multiplatform")
 }
 
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
 dependencies {
 }
 
 kotlin {
-    jvm() {
-        withJava()
+    if (desktopEnabled) {
+        jvm() {
+            withJava()
+        }
     }
 
-    sourceSets {
-        commonMain.dependencies {
-            implementation(libs.kotlinStdlibCommon)
-            implementation(project(":compose:ui:ui-util"))
-            api(project(":compose:foundation:foundation"))
-            api(project(":compose:material:material"))
-            api(project(":compose:runtime:runtime"))
-            api(project(":compose:ui:ui"))
-            api(project(":compose:ui:ui-tooling-preview"))
-        }
-
-        jvmMain.dependencies {
-            implementation(libs.kotlinStdlib)
-            implementation(libs.kotlinStdlibJdk8)
-            implementation(libs.kotlinCoroutinesCore)
-        }
-
-        jvmTest {
-            resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
-            resources.srcDirs += "src/jvmTest/res"
-            dependencies {
-                implementation(libs.kotlinCoroutinesTest)
-                implementation(libs.skikoCurrentOs)
-                implementation(project(":compose:ui:ui-test-junit4"))
-                implementation(libs.junit)
-                implementation(libs.truth)
+    if (desktopEnabled) {
+        sourceSets {
+            commonMain.dependencies {
+                implementation(libs.kotlinStdlibCommon)
+                implementation(project(":compose:ui:ui-util"))
+                api(project(":compose:foundation:foundation"))
+                api(project(":compose:material:material"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-tooling-preview"))
             }
+
+            jvmMain.dependencies {
+                implementation(libs.kotlinStdlib)
+                implementation(libs.kotlinStdlibJdk8)
+                implementation(libs.kotlinCoroutinesCore)
+            }
+
+            jvmTest {
+                resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
+                resources.srcDirs += "src/jvmTest/res"
+                dependencies {
+                    implementation(libs.kotlinCoroutinesTest)
+                    implementation(libs.skikoCurrentOs)
+                    implementation(project(":compose:ui:ui-test-junit4"))
+                    implementation(libs.junit)
+                    implementation(libs.truth)
+                }
+            }
+        }
+    }
+}
+
+if (!desktopEnabled) {
+    kotlin {
+        jvm("disabled") {
+            withJava()
         }
     }
 }
@@ -74,8 +91,10 @@
     }
 }
 
-tasks.findByName("jvmTest").configure {
-    systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
+if (desktopEnabled) {
+    tasks.findByName("jvmTest").configure {
+        systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
+    }
 }
 
 androidx {
@@ -127,4 +146,6 @@
     }
 }
 
-BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
+if (desktopEnabled) {
+    BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
+}
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 76b09be..7502716 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -15,94 +15,105 @@
  */
 
 import androidx.build.BuildOnServerKt
+import androidx.build.KmpPlatformsKt
 import androidx.build.SupportConfigKt
 
 plugins {
     id("AndroidXPlugin")
+    id("java")
     id("AndroidXComposePlugin")
     id("kotlin-multiplatform")
 }
 
-dependencies {
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
+if (desktopEnabled) {
+    kotlin {
+        jvm()
+
+        sourceSets {
+            jvmMain {
+                resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
+                resources.srcDirs += "src/jvmMain/res"
+            }
+
+            jvmMain.dependencies {
+                implementation(libs.skikoCurrentOs)
+                implementation(project(":compose:desktop:desktop"))
+            }
+        }
+    }
+
+    task run1(type: JavaExec) {
+        dependsOn(":compose:desktop:desktop:jar")
+        main = "androidx.compose.desktop.examples.example1.Main_jvmKt"
+        systemProperty("skiko.fps.enabled", "true")
+        def compilation = kotlin.jvm().compilations["main"]
+        classpath =
+                compilation.output.allOutputs +
+                        compilation.runtimeDependencyFiles
+    }
+
+    task run2(type: JavaExec) {
+        dependsOn(":compose:desktop:desktop:jar")
+        main = "androidx.compose.desktop.examples.example2.Main_jvmKt"
+        def compilation = kotlin.jvm().compilations["main"]
+        classpath =
+                compilation.output.allOutputs +
+                        compilation.runtimeDependencyFiles
+    }
+
+    task run3(type: JavaExec) {
+        dependsOn(":compose:desktop:desktop:jar")
+        main = "androidx.compose.desktop.examples.popupexample.Main_jvmKt"
+        def compilation = kotlin.jvm().compilations["main"]
+        classpath =
+                compilation.output.allOutputs +
+                        compilation.runtimeDependencyFiles
+    }
+
+    task run4(type: JavaExec) {
+        dependsOn(":compose:desktop:desktop:jar")
+        main = "androidx.compose.desktop.examples.swingexample.Main_jvmKt"
+        def compilation = kotlin.jvm().compilations["main"]
+        classpath =
+                compilation.output.allOutputs +
+                        compilation.runtimeDependencyFiles
+    }
+
+    task runVsync(type: JavaExec) {
+        dependsOn(":compose:desktop:desktop:jar")
+        main = "androidx.compose.desktop.examples.vsynctest.Main_jvmKt"
+        jvmArgs("-verbose:gc")
+        def compilation = kotlin.jvm().compilations["main"]
+        classpath =
+                compilation.output.allOutputs +
+                        compilation.runtimeDependencyFiles
+    }
+
+    task runWindowApi(type: JavaExec) {
+        dependsOn(":compose:desktop:desktop:jar")
+        main = "androidx.compose.desktop.examples.windowapi.Main_jvmKt"
+        def compilation = kotlin.jvm().compilations["main"]
+        systemProperty("skiko.rendering.laf.global", "true")
+        systemProperty("skiko.rendering.useScreenMenuBar", "true")
+        classpath =
+                compilation.output.allOutputs +
+                        compilation.runtimeDependencyFiles
+    }
+
+    task run {
+        dependsOn("run1")
+    }
+
+
+    BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
 }
 
-kotlin {
-    jvm()
-
-    sourceSets {
-        jvmMain {
-            resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
-            resources.srcDirs += "src/jvmMain/res"
-        }
-
-        jvmMain.dependencies {
-            implementation(libs.skikoCurrentOs)
-            implementation(project(":compose:desktop:desktop"))
+if (!desktopEnabled) {
+    kotlin {
+        jvm("disabled") {
+            withJava()
         }
     }
 }
-
-task run1(type: JavaExec) {
-    dependsOn(":compose:desktop:desktop:jar")
-    main = "androidx.compose.desktop.examples.example1.Main_jvmKt"
-    systemProperty("skiko.fps.enabled", "true")
-    def compilation = kotlin.jvm().compilations["main"]
-    classpath =
-        compilation.output.allOutputs +
-        compilation.runtimeDependencyFiles
-}
-
-task run2(type: JavaExec) {
-    dependsOn(":compose:desktop:desktop:jar")
-    main = "androidx.compose.desktop.examples.example2.Main_jvmKt"
-    def compilation = kotlin.jvm().compilations["main"]
-    classpath =
-        compilation.output.allOutputs +
-        compilation.runtimeDependencyFiles
-}
-
-task run3(type: JavaExec) {
-    dependsOn(":compose:desktop:desktop:jar")
-    main = "androidx.compose.desktop.examples.popupexample.Main_jvmKt"
-    def compilation = kotlin.jvm().compilations["main"]
-    classpath =
-        compilation.output.allOutputs +
-        compilation.runtimeDependencyFiles
-}
-
-task run4(type: JavaExec) {
-    dependsOn(":compose:desktop:desktop:jar")
-    main = "androidx.compose.desktop.examples.swingexample.Main_jvmKt"
-    def compilation = kotlin.jvm().compilations["main"]
-    classpath =
-        compilation.output.allOutputs +
-        compilation.runtimeDependencyFiles
-}
-
-task runVsync(type: JavaExec) {
-    dependsOn(":compose:desktop:desktop:jar")
-    main = "androidx.compose.desktop.examples.vsynctest.Main_jvmKt"
-    jvmArgs("-verbose:gc")
-    def compilation = kotlin.jvm().compilations["main"]
-    classpath =
-        compilation.output.allOutputs +
-        compilation.runtimeDependencyFiles
-}
-
-task runWindowApi(type: JavaExec) {
-    dependsOn(":compose:desktop:desktop:jar")
-    main = "androidx.compose.desktop.examples.windowapi.Main_jvmKt"
-    def compilation = kotlin.jvm().compilations["main"]
-    systemProperty("skiko.rendering.laf.global", "true")
-    systemProperty("skiko.rendering.useScreenMenuBar", "true")
-    classpath =
-        compilation.output.allOutputs +
-        compilation.runtimeDependencyFiles
-}
-
-task run {
-    dependsOn("run1")
-}
-
-
-BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
diff --git a/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt b/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt
new file mode 100644
index 0000000..0c32401
--- /dev/null
+++ b/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt
@@ -0,0 +1 @@
+// Give Kotlin a placeholder file to compile when this project is disabled.
\ No newline at end of file
diff --git a/compose/desktop/desktop/src/disabled/Empty.kt b/compose/desktop/desktop/src/disabled/Empty.kt
new file mode 100644
index 0000000..0c32401
--- /dev/null
+++ b/compose/desktop/desktop/src/disabled/Empty.kt
@@ -0,0 +1 @@
+// Give Kotlin a placeholder file to compile when this project is disabled.
\ No newline at end of file
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt
index 3155a96..1655ea6 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt
@@ -28,6 +28,7 @@
 import androidx.compose.foundation.MarqueeAnimationMode
 import androidx.compose.foundation.MarqueeAnimationMode.Companion.Immediately
 import androidx.compose.foundation.MarqueeAnimationMode.Companion.WhileFocused
+import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.focusable
@@ -40,7 +41,6 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.samples.BasicFocusableMarqueeSample
 import androidx.compose.foundation.samples.BasicMarqueeSample
 import androidx.compose.foundation.samples.BasicMarqueeWithFadedEdgesSample
@@ -91,6 +91,7 @@
         Spacer(Modifier.height(16.dp))
         Divider()
         Text("Compose marquees:", style = MaterialTheme.typography.subtitle1)
+
         MarqueeText("short", Modifier.fillMaxWidth())
         listOf(40.dp, 80.dp, 120.dp).forEach {
             MarqueeText("long text in short marquee", Modifier.width(it))
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
index b95aeb5..78597e0 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
@@ -21,25 +21,39 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.pager.HorizontalPager
 import androidx.compose.foundation.pager.PageSize
 import androidx.compose.foundation.pager.VerticalPager
 import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.Button
 import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import kotlin.math.roundToInt
 import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalFoundationApi::class)
@@ -254,4 +268,73 @@
             }
         }
     }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun HorizontalPagerWithScrollableContent() {
+    // This is a sample using NestedScroll and Pager.
+    // We use the toolbar offset changing example from
+    // androidx.compose.ui.samples.NestedScrollConnectionSample
+
+    val pagerState = rememberPagerState { 10 }
+
+    val toolbarHeight = 48.dp
+    val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
+    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
+    val nestedScrollConnection = remember {
+        object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                val delta = available.y
+                val newOffset = toolbarOffsetHeightPx.value + delta
+                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
+                return Offset.Zero
+            }
+        }
+    }
+
+    Box(
+        modifier = Modifier
+            .fillMaxSize()
+            .nestedScroll(nestedScrollConnection)
+    ) {
+        TopAppBar(
+            modifier = Modifier
+                .height(toolbarHeight)
+                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
+            title = { Text("Toolbar offset is ${toolbarOffsetHeightPx.value}") }
+        )
+
+        val paddingOffset =
+            toolbarHeight + with(LocalDensity.current) { toolbarOffsetHeightPx.value.toDp() }
+
+        HorizontalPager(
+            modifier = Modifier.fillMaxSize(),
+            state = pagerState,
+            contentPadding = PaddingValues(top = paddingOffset)
+        ) {
+            Column(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .verticalScroll(rememberScrollState())
+            ) {
+                repeat(20) {
+                    Box(
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .height(64.dp)
+                            .padding(4.dp)
+                            .background(if (it % 2 == 0) Color.Black else Color.Yellow),
+                        contentAlignment = Alignment.Center
+                    ) {
+                        Text(
+                            text = it.toString(),
+                            color = if (it % 2 != 0) Color.Black else Color.Yellow
+                        )
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt
index 37cd6eb..ff170d3 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
@@ -40,15 +41,25 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.toPixelMap
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -125,7 +136,8 @@
 
     @Suppress("UnnecessaryOptInAnnotation")
     @OptIn(ExperimentalTestApi::class)
-    @Test fun animates_whenAnimationsDisabledBySystem() {
+    @Test
+    fun animates_whenAnimationsDisabledBySystem() {
         motionDurationScale.scaleFactor = 0f
 
         rule.setContent {
@@ -1041,6 +1053,112 @@
         }
     }
 
+    @Test
+    fun intrinsicsCalculations() {
+        val childMinIntrinsicWidth = 10
+        val childMaxIntrinsicWidth = 20
+        val childMinIntrinsicHeight = 30
+        val childMaxIntrinsicHeight = 40
+        val fixedIntrinsicsMeasurePolicy = object : MeasurePolicy {
+            override fun MeasureScope.measure(
+                measurables: List<Measurable>,
+                constraints: Constraints
+            ): MeasureResult = layout(0, 0) {}
+
+            override fun IntrinsicMeasureScope.minIntrinsicWidth(
+                measurables: List<IntrinsicMeasurable>,
+                height: Int
+            ): Int = childMinIntrinsicWidth
+
+            override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+                measurables: List<IntrinsicMeasurable>,
+                height: Int
+            ): Int = childMaxIntrinsicWidth
+
+            override fun IntrinsicMeasureScope.minIntrinsicHeight(
+                measurables: List<IntrinsicMeasurable>,
+                width: Int
+            ): Int = childMinIntrinsicHeight
+
+            override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+                measurables: List<IntrinsicMeasurable>,
+                width: Int
+            ): Int = childMaxIntrinsicHeight
+        }
+        var minIntrinsicWidth = -1
+        var maxIntrinsicWidth = -1
+        var minIntrinsicHeight = -1
+        var maxIntrinsicHeight = -1
+
+        rule.setContent {
+            Layout(
+                modifier = Modifier
+                    .layout { measurable, _ ->
+                        minIntrinsicWidth = measurable.minIntrinsicWidth(0)
+                        maxIntrinsicWidth = measurable.maxIntrinsicWidth(0)
+                        minIntrinsicHeight = measurable.minIntrinsicHeight(0)
+                        maxIntrinsicHeight = measurable.maxIntrinsicHeight(0)
+                        layout(0, 0) {}
+                    }
+                    .basicMarqueeWithTestParams(),
+                measurePolicy = fixedIntrinsicsMeasurePolicy
+            )
+        }
+
+        rule.runOnIdle {
+            assertThat(minIntrinsicWidth).isEqualTo(0)
+            assertThat(maxIntrinsicWidth).isEqualTo(childMaxIntrinsicWidth)
+            assertThat(minIntrinsicHeight).isEqualTo(childMinIntrinsicHeight)
+            assertThat(maxIntrinsicHeight).isEqualTo(childMaxIntrinsicHeight)
+        }
+    }
+
+    /** See b/278729564. */
+    @Test
+    fun readingIntrinsicsDoesntCauseMeasureLoop() {
+        var outerMeasures = 0
+
+        rule.setContent {
+            Box(
+                Modifier
+                    .width(10.dp)
+                    .layout { measurable, constraints ->
+                        outerMeasures++
+
+                        // Querying the intrinsics should _not_ cause measure to be invalidated on
+                        // the next frame.
+                        measurable.maxIntrinsicWidth(0)
+
+                        val placeable = measurable.measure(constraints)
+                        layout(placeable.width, placeable.height) {
+                            placeable.place(0, 0)
+                        }
+                    }
+                    .basicMarqueeWithTestParams(
+                        iterations = Int.MAX_VALUE,
+                        initialDelayMillis = 0,
+                        delayMillis = 0,
+                        animationMode = Immediately
+                    )
+            ) {
+                BasicText(text = "the quick brown fox jumped over the lazy dogs")
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(outerMeasures).isEqualTo(2)
+        }
+
+        // Let the animation run for a few frames.
+        repeat(10) {
+            rule.mainClock.advanceTimeByFrame()
+        }
+
+        rule.runOnIdle {
+            assertThat(outerMeasures).isEqualTo(2)
+        }
+    }
+
     private fun testAnimationContinuity(
         resetsAnimation: Boolean,
         modifier1: Modifier,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
index 80cc4f9..1c1eb34 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
@@ -57,6 +57,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import kotlin.test.assertTrue
 import kotlinx.coroutines.CoroutineScope
 
 @OptIn(ExperimentalFoundationApi::class)
@@ -336,6 +337,20 @@
         rule.onNodeWithTag("$pageToVerifyPosition")
             .assertPositionInRootIsEqualTo(left + leftContentPadding, top + topContentPadding)
     }
+
+    internal fun runAndWaitForPageSettling(block: () -> Unit) {
+        block()
+        rule.mainClock.advanceTimeUntil {
+            pagerState.currentPageOffsetFraction != 0.0f
+        } // wait for first move from drag
+        rule.mainClock.advanceTimeUntil {
+            pagerState.currentPageOffsetFraction == 0.0f
+        } // wait for fling settling
+        // pump the clock twice and check we're still settled.
+        rule.mainClock.advanceTimeByFrame()
+        rule.mainClock.advanceTimeByFrame()
+        assertTrue { pagerState.currentPageOffsetFraction == 0.0f }
+    }
 }
 
 class ParamConfig(
@@ -358,7 +373,7 @@
 
 internal const val PagerTestTag = "pager"
 internal const val DefaultPageCount = 20
-internal const val DefaultAnimationRepetition = 3
+internal const val DefaultAnimationRepetition = 2
 internal val TestOrientation = listOf(Orientation.Vertical, Orientation.Horizontal)
 internal val AllOrientationsParams = mutableListOf<ParamConfig>().apply {
     for (orientation in TestOrientation) {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
index 6531eba..ef8e8fa 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
 import androidx.test.filters.LargeTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -35,6 +36,11 @@
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
+    @Before
+    fun setUp() {
+        rule.mainClock.autoAdvance = false
+    }
+
     @Test
     fun swipeForwardAndBackward_verifyPagesAreLaidOutCorrectly() {
         // Arrange
@@ -45,13 +51,14 @@
         repeat(DefaultAnimationRepetition) {
             rule.onNodeWithTag(it.toString()).assertIsDisplayed()
             confirmPageIsInCorrectPosition(it)
-            rule.onNodeWithTag(it.toString()).performTouchInput {
-                swipeWithVelocityAcrossMainAxis(
-                    with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
-                    delta
-                )
+            runAndWaitForPageSettling {
+                rule.onNodeWithTag(it.toString()).performTouchInput {
+                    swipeWithVelocityAcrossMainAxis(
+                        with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
+                        delta
+                    )
+                }
             }
-            rule.waitForIdle()
         }
 
         // Act - backward
@@ -59,13 +66,14 @@
             val countDown = DefaultAnimationRepetition - it
             rule.onNodeWithTag(countDown.toString()).assertIsDisplayed()
             confirmPageIsInCorrectPosition(countDown)
-            rule.onNodeWithTag(countDown.toString()).performTouchInput {
-                swipeWithVelocityAcrossMainAxis(
-                    with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
-                    delta * -1f
-                )
+            runAndWaitForPageSettling {
+                rule.onNodeWithTag(countDown.toString()).performTouchInput {
+                    swipeWithVelocityAcrossMainAxis(
+                        with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
+                        delta * -1f
+                    )
+                }
             }
-            rule.waitForIdle()
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
index aaf1a6f..5967023 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.test.performTouchInput
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -33,6 +34,11 @@
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
+    @Before
+    fun setUp() {
+        rule.mainClock.autoAdvance = false
+    }
+
     @Test
     fun offscreenPageLimitIsUsed_shouldPlaceMoreItemsThanVisibleOnesAsWeScroll() {
         // Arrange
@@ -45,11 +51,12 @@
 
         repeat(DefaultAnimationRepetition) {
             // Act
-            onPager().performTouchInput {
-                swipeWithVelocityAcrossMainAxis(0f, delta)
+            runAndWaitForPageSettling {
+                onPager().performTouchInput {
+                    swipeWithVelocityAcrossMainAxis(0f, delta)
+                }
             }
 
-            rule.waitForIdle()
             // Next page was placed
             rule.runOnIdle {
                 Truth.assertThat(placed).contains(
@@ -58,7 +65,7 @@
                 )
             }
         }
-        rule.waitForIdle()
+
         confirmPageIsInCorrectPosition(pagerState.currentPage)
     }
 
@@ -102,7 +109,6 @@
         )
 
         // Assert
-        rule.waitForIdle()
         val firstVisible = pagerState.layoutInfo.visiblePagesInfo.first().index
         val lastVisible = pagerState.layoutInfo.visiblePagesInfo.last().index
         Truth.assertThat(placed).doesNotContain(firstVisible - 1)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
index 70cc0a6..103f9c7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
@@ -32,6 +32,7 @@
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -43,6 +44,11 @@
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
+    @Before
+    fun setUp() {
+        rule.mainClock.autoAdvance = false
+    }
+
     @Test
     fun swipeWithLowVelocity_positionalThresholdLessThanDefaultThreshold_shouldBounceBack() {
         // Arrange
@@ -51,26 +57,28 @@
         val delta = pagerSize * swipeValue * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
         confirmPageIsInCorrectPosition(5)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -89,26 +97,28 @@
         val delta = pagerSize * swipeValue * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
         confirmPageIsInCorrectPosition(5)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -127,26 +137,28 @@
         val delta = pagerSize * swipeValue * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
         confirmPageIsInCorrectPosition(5)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -163,26 +175,28 @@
         val delta = (2.4f * pageSize) * scrollForwardSign // 2.4 pages
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("4").assertIsDisplayed()
         confirmPageIsInCorrectPosition(4)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("2").assertIsDisplayed()
@@ -197,26 +211,28 @@
         val delta = pagerSize * swipeValue * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("6").assertIsDisplayed()
         confirmPageIsInCorrectPosition(6)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -235,26 +251,28 @@
         val delta = pagerSize * swipeValue * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("6").assertIsDisplayed()
         confirmPageIsInCorrectPosition(6)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -275,26 +293,28 @@
         val delta = 2.6f * pageSize * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
         confirmPageIsInCorrectPosition(5)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("2").assertIsDisplayed()
@@ -313,26 +333,28 @@
         val delta = pagerSize * swipeValue * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("6").assertIsDisplayed()
         confirmPageIsInCorrectPosition(6)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -351,26 +373,28 @@
         val delta = pagerSize * 0.4f * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
         confirmPageIsInCorrectPosition(5)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -385,26 +409,28 @@
         val delta = pagerSize * 0.4f * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("6").assertIsDisplayed()
         confirmPageIsInCorrectPosition(6)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -424,26 +450,28 @@
         val delta = pagerSize * 0.4f * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("6").assertIsDisplayed()
         confirmPageIsInCorrectPosition(6)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -458,26 +486,28 @@
         val delta = pagerSize * 0.8f * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("6").assertIsDisplayed()
         confirmPageIsInCorrectPosition(6)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta * -1
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta * -1
+                )
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         rule.onNodeWithTag("5").assertIsDisplayed()
@@ -492,10 +522,11 @@
         val delta = pagerSize * 1.4f * scrollForwardSign
 
         // Act - forward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(0f, delta)
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(0f, delta)
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         assertThat(pagerState.currentPage).isAtMost(7)
@@ -503,10 +534,11 @@
         confirmPageIsInCorrectPosition(pagerState.currentPage)
 
         // Act - backward
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(0f, delta * -1)
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(0f, delta * -1)
+            }
         }
-        rule.waitForIdle()
 
         // Assert
         assertThat(pagerState.currentPage).isAtLeast(5)
@@ -621,8 +653,8 @@
                     initialPage = initialPage,
                     initialPageOffsetFraction = 0f
                 ) {
-                   10
-                }
+                    10
+                }.also { pagerState = it }
             }
 
             HorizontalOrVerticalPager(
@@ -636,39 +668,41 @@
             }
         }
         val delta = pageSize * 0.4f * scrollForwardSign
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
-
-        rule.waitForIdle()
         rule.onNodeWithTag("1").assertIsDisplayed()
         confirmPageIsInCorrectPosition(1)
 
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
 
-        rule.waitForIdle()
         rule.onNodeWithTag("2").assertIsDisplayed()
         confirmPageIsInCorrectPosition(2)
 
         rule.runOnIdle { initialPage = 1 }
 
         rule.waitForIdle()
-        onPager().performTouchInput {
-            swipeWithVelocityAcrossMainAxis(
-                with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
-                delta
-            )
+        runAndWaitForPageSettling {
+            onPager().performTouchInput {
+                swipeWithVelocityAcrossMainAxis(
+                    with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+                    delta
+                )
+            }
         }
 
-        rule.waitForIdle()
         confirmPageIsInCorrectPosition(2)
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateNonGestureScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateNonGestureScrollingTest.kt
new file mode 100644
index 0000000..7bed3fc
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateNonGestureScrollingTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2023 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.pager
+
+import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.StateRestorationTester
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import kotlin.test.assertFalse
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class PagerStateNonGestureScrollingTest(val config: ParamConfig) : BasePagerTest(config) {
+    @Test
+    fun pagerStateNotAttached_shouldReturnDefaultValues_andChangeAfterAttached() = runBlocking {
+        // Arrange
+        val state = PagerStateImpl(5, 0.2f) { DefaultPageCount }
+
+        Truth.assertThat(state.currentPage).isEqualTo(5)
+        Truth.assertThat(state.currentPageOffsetFraction).isEqualTo(0.2f)
+
+        val currentPage = derivedStateOf { state.currentPage }
+        val currentPageOffsetFraction = derivedStateOf { state.currentPageOffsetFraction }
+
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier
+                    .fillMaxSize()
+                    .testTag(PagerTestTag)
+                    .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
+                pageSize = PageSize.Fill,
+                reverseLayout = config.reverseLayout,
+                pageSpacing = config.pageSpacing,
+                contentPadding = config.mainAxisContentPadding,
+            ) {
+                Page(index = it)
+            }
+        }
+
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.scrollToPage(state.currentPage + 1)
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(currentPage.value).isEqualTo(6)
+            Truth.assertThat(currentPageOffsetFraction.value).isEqualTo(0.0f)
+        }
+    }
+
+    @Test
+    fun initialPageOnPagerState_shouldDisplayThatPageFirst() {
+        // Arrange
+
+        // Act
+        createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
+
+        // Assert
+        rule.onNodeWithTag("4").assertDoesNotExist()
+        rule.onNodeWithTag("5").assertIsDisplayed()
+        rule.onNodeWithTag("6").assertDoesNotExist()
+        confirmPageIsInCorrectPosition(pagerState.currentPage)
+    }
+
+    @Test
+    fun testStateRestoration() {
+        // Arrange
+        val tester = StateRestorationTester(rule)
+        lateinit var state: PagerState
+        tester.setContent {
+            state = rememberPagerState(pageCount = { DefaultPageCount })
+            scope = rememberCoroutineScope()
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.fillMaxSize()
+            ) {
+                Page(it)
+            }
+        }
+
+        // Act
+        rule.runOnIdle {
+            scope.launch {
+                state.scrollToPage(5)
+            }
+            runBlocking {
+                state.scroll {
+                    scrollBy(50f)
+                }
+            }
+        }
+
+        val previousPage = state.currentPage
+        val previousOffset = state.currentPageOffsetFraction
+        tester.emulateSavedInstanceStateRestore()
+
+        // Assert
+        rule.runOnIdle {
+            Truth.assertThat(state.currentPage).isEqualTo(previousPage)
+            Truth.assertThat(state.currentPageOffsetFraction).isEqualTo(previousOffset)
+        }
+    }
+
+    @Test
+    fun currentPageOffsetFraction_shouldNeverBeNan() {
+        rule.setContent {
+            val state = rememberPagerState(pageCount = { 10 })
+            // Read state in composition, should never be Nan
+            assertFalse { state.currentPageOffsetFraction.isNaN() }
+            HorizontalOrVerticalPager(state = state) {
+                Page(index = it)
+            }
+        }
+    }
+
+    @Test
+    fun calculatePageCountOffset_shouldBeBasedOnCurrentPage() {
+        val pageToOffsetCalculations = mutableMapOf<Int, Float>()
+        createPager(modifier = Modifier.fillMaxSize(), pageSize = { PageSize.Fixed(20.dp) }) {
+            pageToOffsetCalculations[it] = pagerState.getOffsetFractionForPage(it)
+            Page(index = it)
+        }
+
+        for ((page, offset) in pageToOffsetCalculations) {
+            val currentPage = pagerState.currentPage
+            val currentPageOffset = pagerState.currentPageOffsetFraction
+            Truth.assertThat(offset).isEqualTo((currentPage - page) + currentPageOffset)
+        }
+    }
+
+    @Test
+    fun scrollToPage_usingLaunchedEffect() {
+
+        createPager(additionalContent = {
+            LaunchedEffect(pagerState) {
+                pagerState.scrollToPage(10)
+            }
+        })
+
+        Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+        confirmPageIsInCorrectPosition(10)
+    }
+
+    @Test
+    fun scrollToPageWithOffset_usingLaunchedEffect() {
+        createPager(additionalContent = {
+            LaunchedEffect(pagerState) {
+                pagerState.scrollToPage(10, 0.4f)
+            }
+        })
+
+        Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+        confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
+    }
+
+    @Test
+    fun animatedScrollToPage_usingLaunchedEffect() {
+
+        createPager(additionalContent = {
+            LaunchedEffect(pagerState) {
+                pagerState.animateScrollToPage(10)
+            }
+        })
+
+        Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+        confirmPageIsInCorrectPosition(10)
+    }
+
+    @Test
+    fun animatedScrollToPageWithOffset_usingLaunchedEffect() {
+
+        createPager(additionalContent = {
+            LaunchedEffect(pagerState) {
+                pagerState.animateScrollToPage(10, 0.4f)
+            }
+        })
+
+        Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+        confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
+    }
+
+    @Test
+    fun animatedScrollToPage_viewPortNumberOfPages_usingLaunchedEffect_shouldNotPlaceALlPages() {
+
+        createPager(additionalContent = {
+            LaunchedEffect(pagerState) {
+                pagerState.animateScrollToPage(DefaultPageCount - 1)
+            }
+        })
+
+        // Assert
+        rule.runOnIdle {
+            Truth.assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
+            Truth.assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
+            Truth.assertThat(placed).doesNotContain(DefaultPageCount / 2)
+            Truth.assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
+        }
+        confirmPageIsInCorrectPosition(pagerState.currentPage)
+    }
+
+    @Test
+    fun scrollTo_beforeFirstLayout_shouldWaitForStateAndLayoutSetting() {
+        // Arrange
+
+        rule.mainClock.autoAdvance = false
+
+        // Act
+        createPager(modifier = Modifier.fillMaxSize(), additionalContent = {
+            LaunchedEffect(pagerState) {
+                pagerState.scrollToPage(5)
+            }
+        })
+
+        // Assert
+        Truth.assertThat(pagerState.currentPage).isEqualTo(5)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = mutableListOf<ParamConfig>().apply {
+            for (orientation in TestOrientation) {
+                add(ParamConfig(orientation = orientation))
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
index 9bf833a..fbcbe8b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
@@ -22,21 +22,13 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
-import androidx.compose.ui.unit.dp
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
-import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -52,43 +44,6 @@
 class PagerStateTest(val config: ParamConfig) : BasePagerTest(config) {
 
     @Test
-    fun pagerStateNotAttached_shouldReturnDefaultValues_andChangeAfterAttached() = runBlocking {
-        // Arrange
-        val state = PagerStateImpl(5, 0.2f) { DefaultPageCount }
-
-        assertThat(state.currentPage).isEqualTo(5)
-        assertThat(state.currentPageOffsetFraction).isEqualTo(0.2f)
-
-        val currentPage = derivedStateOf { state.currentPage }
-        val currentPageOffsetFraction = derivedStateOf { state.currentPageOffsetFraction }
-
-        rule.setContent {
-            HorizontalOrVerticalPager(
-                state = state,
-                modifier = Modifier
-                    .fillMaxSize()
-                    .testTag(PagerTestTag)
-                    .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
-                pageSize = PageSize.Fill,
-                reverseLayout = config.reverseLayout,
-                pageSpacing = config.pageSpacing,
-                contentPadding = config.mainAxisContentPadding,
-            ) {
-                Page(index = it)
-            }
-        }
-
-        withContext(Dispatchers.Main + AutoTestFrameClock()) {
-            state.scrollToPage(state.currentPage + 1)
-        }
-
-        rule.runOnIdle {
-            assertThat(currentPage.value).isEqualTo(6)
-            assertThat(currentPageOffsetFraction.value).isEqualTo(0.0f)
-        }
-    }
-
-    @Test
     fun scrollToPage_shouldPlacePagesCorrectly() = runBlocking {
         // Arrange
         createPager(modifier = Modifier.fillMaxSize())
@@ -96,9 +51,11 @@
         // Act and Assert
         repeat(DefaultAnimationRepetition) {
             assertThat(pagerState.currentPage).isEqualTo(it)
+            val nextPage = pagerState.currentPage + 1
             withContext(Dispatchers.Main + AutoTestFrameClock()) {
-                pagerState.scrollToPage(pagerState.currentPage + 1)
+                pagerState.scrollToPage(nextPage)
             }
+            rule.mainClock.advanceTimeUntil { pagerState.currentPage == nextPage }
             confirmPageIsInCorrectPosition(pagerState.currentPage)
         }
     }
@@ -179,9 +136,11 @@
         // Act and Assert
         repeat(DefaultAnimationRepetition) {
             assertThat(pagerState.currentPage).isEqualTo(it)
+            val nextPage = pagerState.currentPage + 1
             withContext(Dispatchers.Main + AutoTestFrameClock()) {
-                pagerState.animateScrollToPage(pagerState.currentPage + 1)
+                pagerState.animateScrollToPage(nextPage)
             }
+            rule.mainClock.advanceTimeUntil { pagerState.currentPage == nextPage }
             confirmPageIsInCorrectPosition(pagerState.currentPage)
         }
     }
@@ -277,31 +236,6 @@
     }
 
     @Test
-    fun scrollToPage_usingLaunchedEffect() {
-
-        createPager(additionalContent = {
-            LaunchedEffect(pagerState) {
-                pagerState.scrollToPage(10)
-            }
-        })
-        rule.waitForIdle()
-        assertThat(pagerState.currentPage).isEqualTo(10)
-        confirmPageIsInCorrectPosition(10)
-    }
-
-    @Test
-    fun scrollToPageWithOffset_usingLaunchedEffect() {
-        createPager(additionalContent = {
-            LaunchedEffect(pagerState) {
-                pagerState.scrollToPage(10, 0.4f)
-            }
-        })
-        rule.waitForIdle()
-        assertThat(pagerState.currentPage).isEqualTo(10)
-        confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
-    }
-
-    @Test
     fun animateScrollToPage_shouldCoerceWithinRange() = runBlocking {
         // Arrange
 
@@ -345,69 +279,26 @@
     @Test
     fun animateScrollToPage_withPassedAnimation() = runBlocking {
         // Arrange
-
+        rule.mainClock.autoAdvance = false
         createPager(modifier = Modifier.fillMaxSize())
         val differentAnimation: AnimationSpec<Float> = tween()
 
         // Act and Assert
         repeat(DefaultAnimationRepetition) {
             assertThat(pagerState.currentPage).isEqualTo(it)
+            val nextPage = pagerState.currentPage + 1
             withContext(Dispatchers.Main + AutoTestFrameClock()) {
                 pagerState.animateScrollToPage(
-                    pagerState.currentPage + 1,
+                    nextPage,
                     animationSpec = differentAnimation
                 )
             }
+            rule.mainClock.advanceTimeUntil { pagerState.currentPage == nextPage }
             confirmPageIsInCorrectPosition(pagerState.currentPage)
         }
     }
 
     @Test
-    fun animatedScrollToPage_usingLaunchedEffect() {
-
-        createPager(additionalContent = {
-            LaunchedEffect(pagerState) {
-                pagerState.animateScrollToPage(10)
-            }
-        })
-        rule.waitForIdle()
-        assertThat(pagerState.currentPage).isEqualTo(10)
-        confirmPageIsInCorrectPosition(10)
-    }
-
-    @Test
-    fun animatedScrollToPageWithOffset_usingLaunchedEffect() {
-
-        createPager(additionalContent = {
-            LaunchedEffect(pagerState) {
-                pagerState.animateScrollToPage(10, 0.4f)
-            }
-        })
-        rule.waitForIdle()
-        assertThat(pagerState.currentPage).isEqualTo(10)
-        confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
-    }
-
-    @Test
-    fun animatedScrollToPage_viewPortNumberOfPages_usingLaunchedEffect_shouldNotPlaceALlPages() {
-
-        createPager(additionalContent = {
-            LaunchedEffect(pagerState) {
-                pagerState.animateScrollToPage(DefaultPageCount - 1)
-            }
-        })
-        rule.waitForIdle()
-        // Assert
-        rule.runOnIdle {
-            assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
-            assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
-            assertThat(placed).doesNotContain(DefaultPageCount / 2)
-            assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
-        }
-        confirmPageIsInCorrectPosition(pagerState.currentPage)
-    }
-
-    @Test
     fun currentPage_shouldChangeWhenClosestPageToSnappedPositionChanges() {
         // Arrange
 
@@ -783,103 +674,6 @@
         }
     }
 
-    @Test
-    fun initialPageOnPagerState_shouldDisplayThatPageFirst() {
-        // Arrange
-
-        // Act
-        createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
-
-        // Assert
-        rule.onNodeWithTag("4").assertDoesNotExist()
-        rule.onNodeWithTag("5").assertIsDisplayed()
-        rule.onNodeWithTag("6").assertDoesNotExist()
-        confirmPageIsInCorrectPosition(pagerState.currentPage)
-    }
-
-    @Test
-    fun testStateRestoration() {
-        // Arrange
-        val tester = StateRestorationTester(rule)
-        lateinit var state: PagerState
-        tester.setContent {
-            state = rememberPagerState(pageCount = { DefaultPageCount })
-            scope = rememberCoroutineScope()
-            HorizontalOrVerticalPager(
-                state = state,
-                modifier = Modifier.fillMaxSize()
-            ) {
-                Page(it)
-            }
-        }
-
-        // Act
-        rule.runOnIdle {
-            scope.launch {
-                state.scrollToPage(5)
-            }
-            runBlocking {
-                state.scroll {
-                    scrollBy(50f)
-                }
-            }
-        }
-
-        val previousPage = state.currentPage
-        val previousOffset = state.currentPageOffsetFraction
-        tester.emulateSavedInstanceStateRestore()
-
-        // Assert
-        rule.runOnIdle {
-            assertThat(state.currentPage).isEqualTo(previousPage)
-            assertThat(state.currentPageOffsetFraction).isEqualTo(previousOffset)
-        }
-    }
-
-    @Test
-    fun scrollTo_beforeFirstLayout_shouldWaitForStateAndLayoutSetting() {
-        // Arrange
-
-        rule.mainClock.autoAdvance = false
-
-        // Act
-        createPager(modifier = Modifier.fillMaxSize(), additionalContent = {
-            LaunchedEffect(pagerState) {
-                pagerState.scrollToPage(5)
-            }
-        })
-
-        // Assert
-        assertThat(pagerState.currentPage).isEqualTo(5)
-    }
-
-    @Test
-    fun currentPageOffsetFraction_shouldNeverBeNan() {
-        rule.setContent {
-            val state = rememberPagerState(pageCount = { 10 })
-            // Read state in composition, should never be Nan
-            assertFalse { state.currentPageOffsetFraction.isNaN() }
-            HorizontalOrVerticalPager(state = state) {
-                Page(index = it)
-            }
-        }
-    }
-
-    @Test
-    fun calculatePageCountOffset_shouldBeBasedOnCurrentPage() {
-        val pageToOffsetCalculations = mutableMapOf<Int, Float>()
-        createPager(modifier = Modifier.fillMaxSize(), pageSize = { PageSize.Fixed(20.dp) }) {
-            pageToOffsetCalculations[it] = pagerState.getOffsetFractionForPage(it)
-            Page(index = it)
-        }
-
-        for ((page, offset) in pageToOffsetCalculations) {
-            val currentPage = pagerState.currentPage
-            val currentPageOffset = pagerState.currentPageOffsetFraction
-            assertThat(offset).isEqualTo((currentPage - page) + currentPageOffset)
-        }
-    }
-
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
index 048508f..61d3630 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
@@ -29,34 +29,35 @@
 import androidx.compose.foundation.FixedMotionDurationScale.scaleFactor
 import androidx.compose.foundation.MarqueeAnimationMode.Companion.Immediately
 import androidx.compose.foundation.MarqueeAnimationMode.Companion.WhileFocused
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.MotionDurationScale
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.DrawModifier
+import androidx.compose.ui.focus.FocusEventModifierNode
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.clipRect
 import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
 import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.requireDensity
+import androidx.compose.ui.node.requireLayoutDirection
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.unit.dp
 import kotlin.math.absoluteValue
@@ -64,6 +65,7 @@
 import kotlin.math.roundToInt
 import kotlin.math.sign
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
 // From https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/TextView.java;l=736;drc=6d97d6d7215fef247d1a90e05545cac3676f9212
@@ -138,8 +140,24 @@
     initialDelayMillis: Int = if (animationMode == Immediately) delayMillis else 0,
     spacing: MarqueeSpacing = DefaultMarqueeSpacing,
     velocity: Dp = DefaultMarqueeVelocity
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
+): Modifier = this then MarqueeModifierElement(
+    iterations = iterations,
+    animationMode = animationMode,
+    delayMillis = delayMillis,
+    initialDelayMillis = initialDelayMillis,
+    spacing = spacing,
+    velocity = velocity,
+)
+
+private data class MarqueeModifierElement(
+    private val iterations: Int,
+    private val animationMode: MarqueeAnimationMode,
+    private val delayMillis: Int,
+    private val initialDelayMillis: Int,
+    private val spacing: MarqueeSpacing,
+    private val velocity: Dp,
+) : ModifierNodeElement<MarqueeModifierNode>() {
+    override fun InspectorInfo.inspectableProperties() {
         name = "basicMarquee"
         properties["iterations"] = iterations
         properties["animationMode"] = animationMode
@@ -148,60 +166,92 @@
         properties["spacing"] = spacing
         properties["velocity"] = velocity
     }
-) {
-    val density = LocalDensity.current
-    val layoutDirection = LocalLayoutDirection.current
-    val modifier = remember(
-        iterations,
-        delayMillis,
-        initialDelayMillis,
-        velocity,
-        density,
-        layoutDirection,
-    ) {
-        MarqueeModifier(
+
+    override fun create(): MarqueeModifierNode =
+        MarqueeModifierNode(
             iterations = iterations,
+            animationMode = animationMode,
             delayMillis = delayMillis,
             initialDelayMillis = initialDelayMillis,
-            velocity = velocity * if (layoutDirection == Ltr) 1f else -1f,
-            density = density
+            spacing = spacing,
+            velocity = velocity,
+        )
+
+    override fun update(node: MarqueeModifierNode) {
+        node.update(
+            iterations = iterations,
+            animationMode = animationMode,
+            delayMillis = delayMillis,
+            initialDelayMillis = initialDelayMillis,
+            spacing = spacing,
+            velocity = velocity,
         )
     }
-    modifier.spacing = spacing
-    modifier.animationMode = animationMode
-
-    LaunchedEffect(modifier) {
-        modifier.runAnimation()
-    }
-
-    return@composed modifier
 }
 
-private class MarqueeModifier(
-    private val iterations: Int,
-    private val delayMillis: Int,
-    private val initialDelayMillis: Int,
-    private val velocity: Dp,
-    private val density: Density,
-) : Modifier.Element,
-    LayoutModifier,
-    DrawModifier,
-    @Suppress("DEPRECATION") androidx.compose.ui.focus.FocusEventModifier {
+private class MarqueeModifierNode(
+    private var iterations: Int,
+    animationMode: MarqueeAnimationMode,
+    private var delayMillis: Int,
+    private var initialDelayMillis: Int,
+    spacing: MarqueeSpacing,
+    private var velocity: Dp,
+) : Modifier.Node(),
+    LayoutModifierNode,
+    DrawModifierNode,
+    FocusEventModifierNode {
 
     private var contentWidth by mutableStateOf(0)
     private var containerWidth by mutableStateOf(0)
     private var hasFocus by mutableStateOf(false)
-    var spacing: MarqueeSpacing by mutableStateOf(DefaultMarqueeSpacing)
-    var animationMode: MarqueeAnimationMode by mutableStateOf(Immediately)
+    var spacing: MarqueeSpacing by mutableStateOf(spacing)
+    var animationMode: MarqueeAnimationMode by mutableStateOf(animationMode)
 
     private val offset = Animatable(0f)
-    private val direction = sign(velocity.value)
+    private val direction
+        get() = sign(velocity.value) * when (requireLayoutDirection()) {
+            LayoutDirection.Ltr -> 1
+            LayoutDirection.Rtl -> -1
+        }
     private val spacingPx by derivedStateOf {
         with(spacing) {
-            density.calculateSpacing(contentWidth, containerWidth)
+            requireDensity().calculateSpacing(contentWidth, containerWidth)
         }
     }
 
+    override fun onAttach() {
+        restartAnimation()
+    }
+
+    fun update(
+        iterations: Int,
+        animationMode: MarqueeAnimationMode,
+        delayMillis: Int,
+        initialDelayMillis: Int,
+        spacing: MarqueeSpacing,
+        velocity: Dp,
+    ) {
+        this.spacing = spacing
+        this.animationMode = animationMode
+
+        if (
+            this.iterations != iterations ||
+            this.delayMillis != delayMillis ||
+            this.initialDelayMillis != initialDelayMillis ||
+            this.velocity != velocity
+        ) {
+            this.iterations = iterations
+            this.delayMillis = delayMillis
+            this.initialDelayMillis = initialDelayMillis
+            this.velocity = velocity
+            restartAnimation()
+        }
+    }
+
+    override fun onFocusEvent(focusState: FocusState) {
+        hasFocus = focusState.hasFocus
+    }
+
     override fun MeasureScope.measure(
         measurable: Measurable,
         constraints: Constraints
@@ -217,6 +267,31 @@
         }
     }
 
+    // Override intrinsic calculations to avoid setting state (see b/278729564).
+
+    /** Always returns zero since the marquee has no minimum width. */
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = 0
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = measurable.maxIntrinsicWidth(height)
+
+    /** Ignores width since marquee contents are always measured with infinite width. */
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = measurable.minIntrinsicHeight(Constraints.Infinity)
+
+    /** Ignores width since marquee contents are always measured with infinite width. */
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = measurable.maxIntrinsicHeight(Constraints.Infinity)
+
     override fun ContentDrawScope.draw() {
         val clipOffset = offset.value * direction
         val firstCopyVisible = when (direction) {
@@ -249,11 +324,15 @@
         }
     }
 
-    override fun onFocusEvent(focusState: FocusState) {
-        hasFocus = focusState.hasFocus
+    private fun restartAnimation() {
+        if (isAttached) {
+            coroutineScope.launch {
+                runAnimation()
+            }
+        }
     }
 
-    suspend fun runAnimation() {
+    private suspend fun runAnimation() {
         if (iterations <= 0) {
             // No animation.
             return
@@ -279,7 +358,7 @@
                     initialDelayMillis,
                     delayMillis,
                     velocity,
-                    density
+                    requireDensity()
                 )
 
                 offset.snapTo(0f)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index 025ea33..cb376a7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -70,8 +70,9 @@
  * @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
  * of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
  *
- * Please refer to the sample to learn how to use this API.
+ * Please refer to the samples to learn how to use this API.
  * @sample androidx.compose.foundation.samples.SimpleHorizontalPagerSample
+ * @sample androidx.compose.foundation.samples.HorizontalPagerWithScrollableContent
  *
  * @param state The state to control this pager
  * @param modifier A modifier instance to be applied to this Pager outer layout
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
index aa559c9..d2992f3 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
@@ -109,7 +109,23 @@
     onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     style: TextStyle = LocalTextStyle.current
 ) {
-    // weird coding to avoid groups
+    // TL:DR: profile before you change any line of code in this method
+    //
+    // The call to LocalContentAlpha.current looks like it can be avoided by only calling it in the
+    // last else block but, in 1.5, this causes a control flow group to be created because it would
+    // be a conditional call to a composable function. The call is currently made unconditionally
+    // since the call to LocalContentAlpha.current does not create a group (it is a read-only
+    // composable) and looking up the value in the composition locals map is currently faster than
+    // creating a group to avoid it.
+    //
+    // Similar notes regarding lambda allocations. It appears there's a path to optimize for
+    // zero-allocations in the style-provided color route, but this either introduces a group or a
+    // box depending on how it's coded. It's also possible that allocating a final ColorProducer
+    // subclass with no capture may be a successful optimization, but it appeared slower in initial
+    // profiling.
+    //
+    // If changing ANY LINE OF CODE, please confirm that it's faster or the same speed using
+    // profilers and benchmarks.
     val localContentColor = LocalContentColor.current
     val localContentAlpha = LocalContentAlpha.current
     val overrideColorOrUnspecified: Color = if (color.isSpecified) {
@@ -261,7 +277,23 @@
     onTextLayout: (TextLayoutResult) -> Unit = {},
     style: TextStyle = LocalTextStyle.current
 ) {
-    // weird coding to avoid groups
+    // TL:DR: profile before you change any line of code in this method
+    //
+    // The call to LocalContentAlpha.current looks like it can be avoided by only calling it in the
+    // last else block but, in 1.5, this causes a control flow group to be created because it would
+    // be a conditional call to a composable function. The call is currently made unconditionally
+    // since the call to LocalContentAlpha.current does not create a group (it is a read-only
+    // composable) and looking up the value in the composition locals map is currently faster than
+    // creating a group to avoid it.
+    //
+    // Similar notes regarding lambda allocations. It appears there's a path to optimize for
+    // zero-allocations in the style-provided color route, but this either introduces a group or a
+    // box depending on how it's coded. It's also possible that allocating a final ColorProducer
+    // subclass with no capture may be a successful optimization, but it appeared slower in initial
+    // profiling.
+    //
+    // If changing ANY LINE OF CODE, please confirm that it's faster or the same speed using
+    // profilers and benchmarks.
     val localContentColor = LocalContentColor.current
     val localContentAlpha = LocalContentAlpha.current
     val overrideColorOrUnspecified = if (color.isSpecified) {
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index d2b53fb..1f5b379 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -22,7 +22,8 @@
   }
 
   public final class AndroidMenu_androidKt {
-    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index c580cf9..b2471cb 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -23,7 +23,8 @@
   }
 
   public final class AndroidMenu_androidKt {
-    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
@@ -511,7 +512,7 @@
   }
 
   @androidx.compose.material3.ExperimentalMaterial3Api public interface ExposedDropdownMenuBoxScope {
-    method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
     method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
     method public androidx.compose.ui.Modifier menuAnchor(androidx.compose.ui.Modifier);
   }
@@ -987,6 +988,7 @@
   @androidx.compose.runtime.Stable public final class SliderDefaults {
     method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
     method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
     method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
     field public static final androidx.compose.material3.SliderDefaults INSTANCE;
   }
@@ -995,7 +997,8 @@
     method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
     method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> track, optional int steps);
     method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> track, optional int steps);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional int steps, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(androidx.compose.material3.SliderState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track);
   }
 
   @androidx.compose.runtime.Stable public final class SliderPositions {
@@ -1006,6 +1009,20 @@
     property public final float[] tickFractions;
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SliderState {
+    ctor public SliderState(optional float initialValue, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? initialOnValueChange, optional int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
+    method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnValueChangeFinished();
+    method public int getSteps();
+    method public float getValue();
+    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getValueRange();
+    method public void setOnValueChangeFinished(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+    method public void setValue(float);
+    property public final kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished;
+    property public final int steps;
+    property public final float value;
+    property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange;
+  }
+
   @androidx.compose.runtime.Stable public interface SnackbarData {
     method public void dismiss();
     method public androidx.compose.material3.SnackbarVisuals getVisuals();
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index d2b53fb..1f5b379 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -22,7 +22,8 @@
   }
 
   public final class AndroidMenu_androidKt {
-    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index e5369cc..1d6480b 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -81,6 +81,7 @@
 import androidx.compose.material3.samples.LeadingIconTabs
 import androidx.compose.material3.samples.LinearProgressIndicatorSample
 import androidx.compose.material3.samples.MenuSample
+import androidx.compose.material3.samples.MenuWithScrollStateSample
 import androidx.compose.material3.samples.ModalBottomSheetSample
 import androidx.compose.material3.samples.ModalNavigationDrawerSample
 import androidx.compose.material3.samples.NavigationBarItemWithBadge
@@ -618,6 +619,13 @@
         MenuSample()
     },
     Example(
+        name = ::MenuWithScrollStateSample.name,
+        description = MenusExampleDescription,
+        sourceUrl = MenusExampleSourceUrl
+    ) {
+        MenuWithScrollStateSample()
+    },
+    Example(
         name = ::ExposedDropdownMenuSample.name,
         description = MenusExampleDescription,
         sourceUrl = MenusExampleSourceUrl
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
index 0c5f3c3..b6e3445 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.MoreVert
 import androidx.compose.material.icons.outlined.Edit
@@ -32,6 +33,7 @@
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -47,7 +49,11 @@
 fun MenuSample() {
     var expanded by remember { mutableStateOf(false) }
 
-    Box(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
+    Box(
+        modifier = Modifier
+            .fillMaxSize()
+            .wrapContentSize(Alignment.TopStart)
+    ) {
         IconButton(onClick = { expanded = true }) {
             Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
         }
@@ -87,3 +93,43 @@
         }
     }
 }
+
+@Preview
+@Sampled
+@Composable
+fun MenuWithScrollStateSample() {
+    var expanded by remember { mutableStateOf(false) }
+    val scrollState = rememberScrollState()
+    Box(
+        modifier = Modifier
+            .fillMaxSize()
+            .wrapContentSize(Alignment.TopStart)
+    ) {
+        IconButton(onClick = { expanded = true }) {
+            Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
+        }
+        DropdownMenu(
+            expanded = expanded,
+            onDismissRequest = { expanded = false },
+            scrollState = scrollState
+        ) {
+            repeat(30) {
+                DropdownMenuItem(
+                    text = { Text("Item ${it + 1}") },
+                    onClick = { /* TODO */ },
+                    leadingIcon = {
+                        Icon(
+                            Icons.Outlined.Edit,
+                            contentDescription = null
+                        )
+                    })
+            }
+        }
+        LaunchedEffect(expanded) {
+            if (expanded) {
+                // Scroll to show the bottom menu items.
+                scrollState.scrollTo(scrollState.maxValue)
+            }
+        }
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
index 154bdd2..56e258c 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
@@ -28,6 +28,7 @@
 import androidx.compose.material3.RangeSlider
 import androidx.compose.material3.Slider
 import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.SliderState
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -88,7 +89,7 @@
             value = sliderPosition,
             onValueChange = { sliderPosition = it },
             valueRange = 0f..5f,
-            steps = 4,
+            steps = 10,
             onValueChangeFinished = {
                 // launch some business logic update with the state you hold
                 // viewModel.updateSelectedSliderValue(sliderPosition)
@@ -110,20 +111,22 @@
 @Sampled
 @Composable
 fun SliderWithCustomTrackAndThumb() {
-    var sliderPosition by remember { mutableStateOf(0f) }
-    val interactionSource = MutableInteractionSource()
-    val colors = SliderDefaults.colors(thumbColor = Color.Red, activeTrackColor = Color.Red)
-    Column {
-        Text(text = sliderPosition.toString())
-        Slider(
-            modifier = Modifier.semantics { contentDescription = "Localized Description" },
-            value = sliderPosition,
-            onValueChange = { sliderPosition = it },
+    val sliderState = remember {
+        SliderState(
             valueRange = 0f..100f,
             onValueChangeFinished = {
                 // launch some business logic update with the state you hold
                 // viewModel.updateSelectedSliderValue(sliderPosition)
-            },
+            }
+        )
+    }
+    val interactionSource = MutableInteractionSource()
+    val colors = SliderDefaults.colors(thumbColor = Color.Red, activeTrackColor = Color.Red)
+    Column {
+        Text(text = sliderState.value.toString())
+        Slider(
+            state = sliderState,
+            modifier = Modifier.semantics { contentDescription = "Localized Description" },
             interactionSource = interactionSource,
             thumb = {
                 SliderDefaults.Thumb(
@@ -131,17 +134,16 @@
                     colors = colors
                 )
             },
-            track = { sliderPositions ->
+            track = {
                 SliderDefaults.Track(
                     colors = colors,
-                    sliderPositions = sliderPositions
+                    sliderState = sliderState
                 )
             }
         )
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Preview
 @Sampled
 @Composable
@@ -162,7 +164,6 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Preview
 @Sampled
 @Composable
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
index 66ae652..24bde2e 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
@@ -22,7 +22,9 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -35,9 +37,11 @@
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertTextContains
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -198,11 +202,17 @@
                         modifier = Modifier.padding(8.dp),
                     ) {
                         TextField(
-                            modifier = Modifier.menuAnchor().then(
-                                if (index == testIndex) Modifier.testTag(TFTag).onSizeChanged {
-                                    textFieldSize = it
-                                } else { Modifier }
-                            ),
+                            modifier = Modifier
+                                .menuAnchor()
+                                .then(
+                                    if (index == testIndex) Modifier
+                                        .testTag(TFTag)
+                                        .onSizeChanged {
+                                            textFieldSize = it
+                                        } else {
+                                        Modifier
+                                    }
+                                ),
                             value = selectedOptionText,
                             onValueChange = { selectedOptionText = it },
                             label = { Text("Label") },
@@ -315,7 +325,10 @@
                             setContent {
                                 Box {
                                     ExposedDropdownMenuBox(expanded = true, onExpandedChange = {}) {
-                                        Box(Modifier.menuAnchor().size(20.dp))
+                                        Box(
+                                            Modifier
+                                                .menuAnchor()
+                                                .size(20.dp))
                                     }
                                 }
                             }
@@ -334,6 +347,49 @@
         // Should not have crashed.
     }
 
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Test
+    fun withScrolledContent() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.fillMaxSize()) {
+                ExposedDropdownMenuBox(
+                    modifier = Modifier.align(Alignment.Center),
+                    expanded = true,
+                    onExpandedChange = { }
+                ) {
+                    val scrollState = rememberScrollState()
+                    TextField(
+                        modifier = Modifier.menuAnchor(),
+                        value = "",
+                        onValueChange = { },
+                        label = { Text("Label") },
+                    )
+                    ExposedDropdownMenu(
+                        expanded = true,
+                        onDismissRequest = { },
+                        scrollState = scrollState
+                    ) {
+                        repeat(100) {
+                            Box(
+                                Modifier
+                                    .testTag("MenuContent ${it + 1}")
+                                    .size(with(LocalDensity.current) { 70.toDp() })
+                            )
+                        }
+                    }
+                    LaunchedEffect(Unit) {
+                        scrollState.scrollTo(scrollState.maxValue)
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+        rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+    }
+
     @Composable
     fun ExposedDropdownMenuForTest(
         expanded: Boolean,
@@ -349,7 +405,9 @@
                 onExpandedChange = { onExpandChange(!expanded) }
             ) {
                 TextField(
-                    modifier = Modifier.menuAnchor().testTag(TFTag)
+                    modifier = Modifier
+                        .menuAnchor()
+                        .testTag(TFTag)
                         .onGloballyPositioned {
                             onTextFieldBoundsChanged?.invoke(it.boundsInRoot())
                         },
@@ -368,9 +426,11 @@
                     colors = ExposedDropdownMenuDefaults.textFieldColors()
                 )
                 ExposedDropdownMenu(
-                    modifier = Modifier.testTag(EDMTag).onGloballyPositioned {
-                        onMenuBoundsChanged?.invoke(it.boundsInRoot())
-                    },
+                    modifier = Modifier
+                        .testTag(EDMTag)
+                        .onGloballyPositioned {
+                            onMenuBoundsChanged?.invoke(it.boundsInRoot())
+                        },
                     expanded = expanded,
                     onDismissRequest = { onExpandChange(false) }
                 ) {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
index 2810336..74c0d5c 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.MutableTransitionState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Edit
 import androidx.compose.material.icons.outlined.Email
@@ -100,10 +101,14 @@
 
     @Composable
     private fun TestMenu(enabledItems: Boolean) {
-        Box(Modifier.testTag(testTag).padding(20.dp), contentAlignment = Alignment.Center) {
+        Box(
+            Modifier
+                .testTag(testTag)
+                .padding(20.dp), contentAlignment = Alignment.Center) {
             DropdownMenuContent(
                 expandedStates = MutableTransitionState(initialState = true),
-                transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
+                transformOriginState = remember { mutableStateOf(TransformOrigin.Center) },
+                scrollState = rememberScrollState()
             ) {
                 DropdownMenuItem(
                     text = { Text("Edit") },
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
index 124eac9..0df58c9 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
@@ -21,6 +21,8 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -28,7 +30,8 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.hasAnyDescendant
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.isPopup
@@ -51,7 +54,6 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTestApi::class)
 class MenuTest {
     @get:Rule
     val rule = createComposeRule()
@@ -61,7 +63,11 @@
         var expanded by mutableStateOf(false)
 
         rule.setContent {
-            Box(Modifier.requiredSize(20.dp).background(color = Color.Blue)) {
+            Box(
+                Modifier
+                    .requiredSize(20.dp)
+                    .background(color = Color.Blue)
+            ) {
                 DropdownMenu(
                     expanded = expanded,
                     onDismissRequest = {}
@@ -103,13 +109,25 @@
     fun menu_hasExpectedSize() {
         rule.setContent {
             with(LocalDensity.current) {
-                Box(Modifier.requiredSize(20.toDp()).background(color = Color.Blue)) {
+                Box(
+                    Modifier
+                        .requiredSize(20.toDp())
+                        .background(color = Color.Blue)
+                ) {
                     DropdownMenu(
                         expanded = true,
                         onDismissRequest = {}
                     ) {
-                        Box(Modifier.testTag("MenuContent1").size(70.toDp()))
-                        Box(Modifier.testTag("MenuContent2").size(130.toDp()))
+                        Box(
+                            Modifier
+                                .testTag("MenuContent1")
+                                .size(70.toDp())
+                        )
+                        Box(
+                            Modifier
+                                .testTag("MenuContent2")
+                                .size(130.toDp())
+                        )
                     }
                 }
             }
@@ -128,6 +146,43 @@
         }
     }
 
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Test
+    fun menu_scrolledContent() {
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .requiredSize(20.toDp())
+                        .background(color = Color.Blue)
+                ) {
+                    val scrollState = rememberScrollState()
+                    DropdownMenu(
+                        expanded = true,
+                        onDismissRequest = {},
+                        scrollState = scrollState
+                    ) {
+                        repeat(100) {
+                            Box(
+                                Modifier
+                                    .testTag("MenuContent ${it + 1}")
+                                    .size(70.toDp())
+                            )
+                        }
+                    }
+                    LaunchedEffect(Unit) {
+                        scrollState.scrollTo(scrollState.maxValue)
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+        rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+    }
+
     @Test
     fun menu_positioning_bottomEnd() {
         val screenWidth = 500
@@ -349,7 +404,9 @@
             DropdownMenuItem(
                 text = { Box(Modifier.requiredSize(40.dp)) },
                 onClick,
-                modifier = Modifier.testTag("MenuItem").clickable(onClick = onClick),
+                modifier = Modifier
+                    .testTag("MenuItem")
+                    .clickable(onClick = onClick),
             )
         }
 
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
index ee8d7f5..0576f34 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
@@ -676,7 +676,7 @@
                 modifier = Modifier.testTag(tag),
                 value = state.value,
                 onValueChange = { state.value = it },
-                thumb = { sliderPositions -> recompositionCounter.OuterContent(sliderPositions) }
+                thumb = { sliderState -> recompositionCounter.OuterContent(sliderState) }
             )
         }
 
@@ -704,7 +704,7 @@
                 modifier = Modifier.testTag(tag),
                 value = state.value,
                 onValueChange = { state.value = it },
-                track = { sliderPositions -> recompositionCounter.OuterContent(sliderPositions) }
+                track = { sliderState -> recompositionCounter.OuterContent(sliderState) }
             )
         }
 
@@ -959,7 +959,6 @@
         }
     }
 
-    @OptIn(ExperimentalMaterial3Api::class)
     @Test
     fun rangeSlider_drag_out_of_bounds_rtl() {
         val state = mutableStateOf(0f..1f)
@@ -1208,8 +1207,8 @@
     @Test
     fun rangeSlider_thumb_recomposition() {
         val state = mutableStateOf(0f..100f)
-        val startRecompositionCounter = SliderRecompositionCounter()
-        val endRecompositionCounter = SliderRecompositionCounter()
+        val startRecompositionCounter = RangeSliderRecompositionCounter()
+        val endRecompositionCounter = RangeSliderRecompositionCounter()
 
         rule.setContent {
             RangeSlider(
@@ -1246,7 +1245,7 @@
     @Test
     fun rangeSlider_track_recomposition() {
         val state = mutableStateOf(0f..100f)
-        val recompositionCounter = SliderRecompositionCounter()
+        val recompositionCounter = RangeSliderRecompositionCounter()
 
         rule.setContent {
             RangeSlider(
@@ -1302,7 +1301,7 @@
 }
 
 @Stable
-class SliderRecompositionCounter {
+class RangeSliderRecompositionCounter {
     var innerRecomposition = 0
     var outerRecomposition = 0
 
@@ -1322,4 +1321,26 @@
         SideEffect { ++innerRecomposition }
         Text("InnerContent: ${sliderPositions.activeRange}")
     }
-}
\ No newline at end of file
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Stable
+class SliderRecompositionCounter {
+    var innerRecomposition = 0
+    var outerRecomposition = 0
+
+    @Composable
+    fun OuterContent(state: SliderState) {
+        SideEffect { ++outerRecomposition }
+        Column {
+            Text("OuterContent")
+            InnerContent(state)
+        }
+    }
+
+    @Composable
+    private fun InnerContent(state: SliderState) {
+        SideEffect { ++innerRecomposition }
+        Text("InnerContent: ${state.value}")
+    }
+}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
index 3a96fdc..0acd46a 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
@@ -17,10 +17,12 @@
 package androidx.compose.material3
 
 import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -67,7 +69,83 @@
  * @param expanded whether the menu is expanded or not
  * @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
  * outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
  * @param offset [DpOffset] to be added to the position of the menu
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Suppress("ModifierParameter")
+@Deprecated(
+    level = DeprecationLevel.HIDDEN,
+    replaceWith = ReplaceWith(
+        expression = "DropdownMenu(expanded,onDismissRequest, modifier, offset, " +
+            "rememberScrollState(), properties, content)",
+        "androidx.compose.foundation.rememberScrollState"
+    ),
+    message = "Replaced by a DropdownMenu function with a ScrollState parameter"
+)
+@Composable
+fun DropdownMenu(
+    expanded: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+    offset: DpOffset = DpOffset(0.dp, 0.dp),
+    properties: PopupProperties = PopupProperties(focusable = true),
+    content: @Composable ColumnScope.() -> Unit
+) = DropdownMenu(
+    expanded = expanded,
+    onDismissRequest = onDismissRequest,
+    modifier = modifier,
+    offset = offset,
+    scrollState = rememberScrollState(),
+    properties = properties,
+    content = content
+)
+
+/**
+ * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a>.
+ *
+ * Menus display a list of choices on a temporary surface. They appear when users interact with a
+ * button, action, or other control.
+ *
+ * ![Dropdown menu image](https://developer.android.com/images/reference/androidx/compose/material3/menu.png)
+ *
+ * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout
+ * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling
+ * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any
+ * space in a layout, as the menu is displayed in a separate window, on top of other content.
+ *
+ * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus. Also note that the [content] is placed inside a scrollable [Column],
+ * so using a [LazyColumn] as the root layout inside [content] is unsupported.
+ *
+ * [onDismissRequest] will be called when the menu should close - for example when there is a
+ * tap outside the menu, or when the back key is pressed.
+ *
+ * [DropdownMenu] changes its positioning depending on the available space, always trying to be
+ * fully visible. It will try to expand horizontally, depending on layout direction, to the end of
+ * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will
+ * try to expand to the bottom of its parent, then from the top of its parent, and then screen
+ * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when
+ * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will
+ * be applied in the direction in which the menu will decide to expand.
+ *
+ * Example usage:
+ * @sample androidx.compose.material3.samples.MenuSample
+ *
+ * Example usage with a [ScrollState] to control the menu items scroll position:
+ * @sample androidx.compose.material3.samples.MenuWithScrollStateSample
+ *
+ * @param expanded whether the menu is expanded or not
+ * @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
+ * outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
+ * @param offset [DpOffset] to be added to the position of the menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
  */
 @Suppress("ModifierParameter")
 @Composable
@@ -76,6 +154,7 @@
     onDismissRequest: () -> Unit,
     modifier: Modifier = Modifier,
     offset: DpOffset = DpOffset(0.dp, 0.dp),
+    scrollState: ScrollState = rememberScrollState(),
     properties: PopupProperties = PopupProperties(focusable = true),
     content: @Composable ColumnScope.() -> Unit
 ) {
@@ -100,6 +179,7 @@
             DropdownMenuContent(
                 expandedStates = expandedStates,
                 transformOriginState = transformOriginState,
+                scrollState = scrollState,
                 modifier = modifier,
                 content = content
             )
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
index 9fe9b8c..b863609 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
@@ -20,6 +20,7 @@
 import android.view.View
 import android.view.ViewTreeObserver
 import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.gestures.awaitEachGesture
 import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -28,6 +29,7 @@
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
 import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material.icons.Icons
@@ -239,6 +241,7 @@
      * @param onDismissRequest called when the user requests to dismiss the menu, such as by
      * tapping outside the menu's bounds
      * @param modifier the [Modifier] to be applied to this menu
+     * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
      * @param content the content of the menu
      */
     @Composable
@@ -246,6 +249,7 @@
         expanded: Boolean,
         onDismissRequest: () -> Unit,
         modifier: Modifier = Modifier,
+        scrollState: ScrollState = rememberScrollState(),
         content: @Composable ColumnScope.() -> Unit
     ) {
         // TODO(b/202810604): use DropdownMenu when PopupProperties constructor is stable
@@ -277,6 +281,7 @@
                 DropdownMenuContent(
                     expandedStates = expandedStates,
                     transformOriginState = transformOriginState,
+                    scrollState = scrollState,
                     modifier = modifier.exposedDropdownSize(),
                     content = content
                 )
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 0ff7c120..318985d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -21,6 +21,7 @@
 import androidx.compose.animation.core.animateFloat
 import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
@@ -34,7 +35,6 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.sizeIn
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.material3.tokens.MenuTokens
@@ -66,6 +66,7 @@
 internal fun DropdownMenuContent(
     expandedStates: MutableTransitionState<Boolean>,
     transformOriginState: MutableState<TransformOrigin>,
+    scrollState: ScrollState,
     modifier: Modifier = Modifier,
     content: @Composable ColumnScope.() -> Unit
 ) {
@@ -133,7 +134,7 @@
             modifier = modifier
                 .padding(vertical = DropdownMenuVerticalPadding)
                 .width(IntrinsicSize.Max)
-                .verticalScroll(rememberScrollState()),
+                .verticalScroll(scrollState),
             content = content
         )
     }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index 1547cab..69c0d59 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.material3
 
-import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.MutatePriority
@@ -27,6 +26,7 @@
 import androidx.compose.foundation.gestures.DraggableState
 import androidx.compose.foundation.gestures.GestureCancellationException
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.PressGestureScope
 import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.gestures.draggable
@@ -52,7 +52,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
@@ -71,6 +70,7 @@
 import androidx.compose.ui.graphics.PointMode
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.PointerInputChange
@@ -143,6 +143,7 @@
  * for this slider. You can create and pass in your own `remember`ed instance to observe
  * [Interaction]s and customize the appearance / behavior of this slider in different states.
  */
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun Slider(
     value: Float,
@@ -156,17 +157,15 @@
     colors: SliderColors = SliderDefaults.colors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
 ) {
-    require(steps >= 0) { "steps should be >= 0" }
-
-    SliderImpl(
+    Slider(
+        value = value,
+        onValueChange = onValueChange,
         modifier = modifier,
         enabled = enabled,
-        interactionSource = interactionSource,
-        onValueChange = onValueChange,
         onValueChangeFinished = onValueChangeFinished,
+        colors = colors,
+        interactionSource = interactionSource,
         steps = steps,
-        value = value,
-        valueRange = valueRange,
         thumb = {
             SliderDefaults.Thumb(
                 interactionSource = interactionSource,
@@ -174,13 +173,14 @@
                 enabled = enabled
             )
         },
-        track = { sliderPositions ->
+        track = { sliderState ->
             SliderDefaults.Track(
                 colors = colors,
                 enabled = enabled,
-                sliderPositions = sliderPositions
+                sliderState = sliderState
             )
-        }
+        },
+        valueRange = valueRange
     )
 }
 
@@ -219,8 +219,6 @@
  * @param enabled controls the enabled state of this slider. When `false`, this component will not
  * respond to user input, and it will appear visually disabled and disabled to accessibility
  * services.
- * @param valueRange range of values that this slider can take. The passed [value] will be coerced
- * to this range.
  * @param onValueChangeFinished called when value change has ended. This should not be used to
  * update the slider value (use [onValueChange] instead), but rather to know when the user has
  * completed selecting a new value by ending a drag or a click.
@@ -229,15 +227,15 @@
  * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
  * for this slider. You can create and pass in your own `remember`ed instance to observe
  * [Interaction]s and customize the appearance / behavior of this slider in different states.
- * @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The lambda
- * receives a [SliderPositions] which is used to obtain the current active track and the tick positions
- * if the slider is discrete.
- * @param track the track to be displayed on the slider, it is placed underneath the thumb. The lambda
- * receives a [SliderPositions] which is used to obtain the current active track and the tick positions
- * if the slider is discrete.
  * @param steps if greater than 0, specifies the amount of discrete allowable values, evenly
  * distributed across the whole value range. If 0, the slider will behave continuously and allow any
  * value from the range specified. Must not be negative.
+ * @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ * @param track the track to be displayed on the slider, it is placed underneath the thumb. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ * @param valueRange range of values that this slider can take. The passed [value] will be coerced
+ * to this range.
  */
 @Composable
 @ExperimentalMaterial3Api
@@ -246,37 +244,125 @@
     onValueChange: (Float) -> Unit,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
-    valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
     onValueChangeFinished: (() -> Unit)? = null,
     colors: SliderColors = SliderDefaults.colors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    thumb: @Composable (SliderPositions) -> Unit = {
+    /*@IntRange(from = 0)*/
+    steps: Int = 0,
+    thumb: @Composable (SliderState) -> Unit = {
         SliderDefaults.Thumb(
             interactionSource = interactionSource,
             colors = colors,
             enabled = enabled
         )
     },
-    track: @Composable (SliderPositions) -> Unit = { sliderPositions ->
+    track: @Composable (SliderState) -> Unit = { sliderState ->
         SliderDefaults.Track(
             colors = colors,
             enabled = enabled,
-            sliderPositions = sliderPositions
+            sliderState = sliderState
         )
     },
-    /*@IntRange(from = 0)*/
-    steps: Int = 0,
+    valueRange: ClosedFloatingPointRange<Float> = 0f..1f
 ) {
-    require(steps >= 0) { "steps should be >= 0" }
+    val state = remember(
+        steps,
+        valueRange
+    ) {
+        SliderState(
+            value,
+            onValueChange,
+            steps,
+            valueRange,
+            onValueChangeFinished
+        )
+    }
+    state.value = value
+    state.onValueChange = onValueChange
+    state.onValueChangeFinished = onValueChangeFinished
 
-    SliderImpl(
-        value = value,
-        onValueChange = onValueChange,
+    Slider(
+        state = state,
         modifier = modifier,
         enabled = enabled,
-        valueRange = valueRange,
-        steps = steps,
-        onValueChangeFinished = onValueChangeFinished,
+        interactionSource = interactionSource,
+        thumb = thumb,
+        track = track
+    )
+}
+
+/**
+ * <a href="https://m3.material.io/components/sliders/overview" class="external" target="_blank">Material Design slider</a>.
+ *
+ * Sliders allow users to make selections from a range of values.
+ *
+ * Sliders reflect a range of values along a bar, from which users may select a single value.
+ * They are ideal for adjusting settings such as volume, brightness, or applying image filters.
+ *
+ * ![Sliders image](https://developer.android.com/images/reference/androidx/compose/material3/sliders.png)
+ *
+ * Use continuous sliders to allow users to make meaningful selections that don’t
+ * require a specific value:
+ *
+ * @sample androidx.compose.material3.samples.SliderSample
+ *
+ * You can allow the user to choose only between predefined set of values by specifying the amount
+ * of steps between min and max values:
+ *
+ * @sample androidx.compose.material3.samples.StepsSliderSample
+ *
+ * Slider using a custom thumb:
+ *
+ * @sample androidx.compose.material3.samples.SliderWithCustomThumbSample
+ *
+ * Slider using custom track and thumb:
+ *
+ * @sample androidx.compose.material3.samples.SliderWithCustomTrackAndThumb
+ *
+ * @param state [SliderState] which contains the slider's current value.
+ * @param modifier the [Modifier] to be applied to this slider
+ * @param enabled controls the enabled state of this slider. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param colors [SliderColors] that will be used to resolve the colors used for this slider in
+ * different states. See [SliderDefaults.colors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this slider. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this slider in different states.
+ * @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ * @param track the track to be displayed on the slider, it is placed underneath the thumb. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ */
+@Composable
+@ExperimentalMaterial3Api
+fun Slider(
+    state: SliderState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: SliderColors = SliderDefaults.colors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    thumb: @Composable (SliderState) -> Unit = {
+        SliderDefaults.Thumb(
+            interactionSource = interactionSource,
+            colors = colors,
+            enabled = enabled
+        )
+    },
+    track: @Composable (SliderState) -> Unit = { sliderState ->
+        SliderDefaults.Track(
+            colors = colors,
+            enabled = enabled,
+            sliderState = sliderState
+        )
+    }
+) {
+    require(state.steps >= 0) { "steps should be >= 0" }
+
+    SliderImpl(
+        state = state,
+        modifier = modifier,
+        enabled = enabled,
         interactionSource = interactionSource,
         thumb = thumb,
         track = track
@@ -478,93 +564,40 @@
     )
 }
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun SliderImpl(
     modifier: Modifier,
+    state: SliderState,
     enabled: Boolean,
     interactionSource: MutableInteractionSource,
-    onValueChange: (Float) -> Unit,
-    onValueChangeFinished: (() -> Unit)?,
-    steps: Int,
-    value: Float,
-    valueRange: ClosedFloatingPointRange<Float>,
-    thumb: @Composable (SliderPositions) -> Unit,
-    track: @Composable (SliderPositions) -> Unit
+    thumb: @Composable (SliderState) -> Unit,
+    track: @Composable (SliderState) -> Unit
 ) {
-    val onValueChangeState = rememberUpdatedState<(Float) -> Unit> {
-        if (it != value) {
-            onValueChange(it)
-        }
-    }
-
-    val tickFractions = remember(steps) {
-        stepsToTickFractions(steps)
-    }
-
-    val thumbWidth = remember { mutableStateOf(ThumbWidth.value) }
-    val totalWidth = remember { mutableStateOf(0) }
-
-    fun scaleToUserValue(minPx: Float, maxPx: Float, offset: Float) =
-        scale(minPx, maxPx, offset, valueRange.start, valueRange.endInclusive)
-
-    fun scaleToOffset(minPx: Float, maxPx: Float, userValue: Float) =
-        scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
-
-    val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
-    val rawOffset = remember { mutableStateOf(scaleToOffset(0f, 0f, value)) }
-    val pressOffset = remember { mutableStateOf(0f) }
-    val coerced = value.coerceIn(valueRange.start, valueRange.endInclusive)
-
-    val positionFraction = calcFraction(valueRange.start, valueRange.endInclusive, coerced)
-    val sliderPositions = remember {
-        SliderPositions(0f..positionFraction, tickFractions)
-    }
-    sliderPositions.activeRange = 0f..positionFraction
-    sliderPositions.tickFractions = tickFractions
-
-    val draggableState = remember(valueRange) {
-        SliderDraggableState {
-            val maxPx = max(totalWidth.value - thumbWidth.value / 2, 0f)
-            val minPx = min(thumbWidth.value / 2, maxPx)
-            rawOffset.value = (rawOffset.value + it + pressOffset.value)
-            pressOffset.value = 0f
-            val offsetInTrack = snapValueToTick(rawOffset.value, tickFractions, minPx, maxPx)
-            onValueChangeState.value.invoke(scaleToUserValue(minPx, maxPx, offsetInTrack))
-        }
-    }
-
-    val gestureEndAction = rememberUpdatedState {
-        if (!draggableState.isDragging) {
-            // check isDragging in case the change is still in progress (touch -> drag case)
-            onValueChangeFinished?.invoke()
-        }
-    }
-
+    state.isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
     val press = Modifier.sliderTapModifier(
-        draggableState,
+        state,
         interactionSource,
-        totalWidth.value,
-        isRtl,
-        rawOffset,
-        gestureEndAction,
-        pressOffset,
         enabled
     )
-
     val drag = Modifier.draggable(
         orientation = Orientation.Horizontal,
-        reverseDirection = isRtl,
+        reverseDirection = state.isRtl,
         enabled = enabled,
         interactionSource = interactionSource,
-        onDragStopped = { _ -> gestureEndAction.value.invoke() },
-        startDragImmediately = draggableState.isDragging,
-        state = draggableState
+        onDragStopped = { state.gestureEndAction() },
+        startDragImmediately = state.draggableState.isDragging,
+        state = state.draggableState
     )
 
     Layout(
         {
-            Box(modifier = Modifier.layoutId(SliderComponents.THUMB)) { thumb(sliderPositions) }
-            Box(modifier = Modifier.layoutId(SliderComponents.TRACK)) { track(sliderPositions) }
+            Box(modifier = Modifier.layoutId(SliderComponents.THUMB)) {
+                thumb(state)
+            }
+            Box(modifier = Modifier.layoutId(SliderComponents.TRACK)) {
+                track(state)
+            }
         },
         modifier = modifier
             .minimumInteractiveComponentSize()
@@ -573,12 +606,8 @@
                 minHeight = SliderTokens.HandleHeight
             )
             .sliderSemantics(
-                value,
-                enabled,
-                onValueChange,
-                onValueChangeFinished,
-                valueRange,
-                steps
+                state,
+                enabled
             )
             .focusable(enabled, interactionSource)
             .then(press)
@@ -600,11 +629,13 @@
         val sliderWidth = thumbPlaceable.width + trackPlaceable.width
         val sliderHeight = max(trackPlaceable.height, thumbPlaceable.height)
 
-        thumbWidth.value = thumbPlaceable.width.toFloat()
-        totalWidth.value = sliderWidth
+        state.updateDimensions(
+            thumbPlaceable.width.toFloat(),
+            sliderWidth
+        )
 
         val trackOffsetX = thumbPlaceable.width / 2
-        val thumbOffsetX = ((trackPlaceable.width) * positionFraction).roundToInt()
+        val thumbOffsetX = ((trackPlaceable.width) * state.coercedValueAsFraction).roundToInt()
         val trackOffsetY = (sliderHeight - trackPlaceable.height) / 2
         val thumbOffsetY = (sliderHeight - thumbPlaceable.height) / 2
 
@@ -1015,9 +1046,10 @@
         val activeTrackColor = colors.trackColor(enabled, active = true)
         val inactiveTickColor = colors.tickColor(enabled, active = false)
         val activeTickColor = colors.tickColor(enabled, active = true)
-        Canvas(modifier
-            .fillMaxWidth()
-            .height(TrackHeight)
+        Canvas(
+            modifier
+                .fillMaxWidth()
+                .height(TrackHeight)
         ) {
             val isRtl = layoutDirection == LayoutDirection.Rtl
             val sliderLeft = Offset(0f, center.y)
@@ -1068,6 +1100,105 @@
                 }
         }
     }
+
+    /**
+     * The Default track for [Slider]
+     *
+     * @param sliderState [SliderState] which is used to obtain the current active track.
+     * @param modifier the [Modifier] to be applied to the track.
+     * @param colors [SliderColors] that will be used to resolve the colors used for this track in
+     * different states. See [SliderDefaults.colors].
+     * @param enabled controls the enabled state of this slider. When `false`, this component will
+     * not respond to user input, and it will appear visually disabled and disabled to
+     * accessibility services.
+     */
+    @Composable
+    @ExperimentalMaterial3Api
+    fun Track(
+        sliderState: SliderState,
+        modifier: Modifier = Modifier,
+        colors: SliderColors = colors(),
+        enabled: Boolean = true
+    ) {
+
+        val inactiveTrackColor by colors.trackColor(enabled, active = false)
+        val activeTrackColor by colors.trackColor(enabled, active = true)
+        val inactiveTickColor by colors.tickColor(enabled, active = false)
+        val activeTickColor by colors.tickColor(enabled, active = true)
+        Canvas(
+            modifier
+                .fillMaxWidth()
+                .height(TrackHeight)
+        ) {
+            drawTrack(
+                sliderState.tickFractions,
+                0f,
+                sliderState.coercedValueAsFraction,
+                inactiveTrackColor,
+                activeTrackColor,
+                inactiveTickColor,
+                activeTickColor
+            )
+        }
+    }
+
+    private fun DrawScope.drawTrack(
+        tickFractions: FloatArray,
+        activeRangeStart: Float,
+        activeRangeEnd: Float,
+        inactiveTrackColor: Color,
+        activeTrackColor: Color,
+        inactiveTickColor: Color,
+        activeTickColor: Color
+    ) {
+        val isRtl = layoutDirection == LayoutDirection.Rtl
+        val sliderLeft = Offset(0f, center.y)
+        val sliderRight = Offset(size.width, center.y)
+        val sliderStart = if (isRtl) sliderRight else sliderLeft
+        val sliderEnd = if (isRtl) sliderLeft else sliderRight
+        val tickSize = TickSize.toPx()
+        val trackStrokeWidth = TrackHeight.toPx()
+        drawLine(
+            inactiveTrackColor,
+            sliderStart,
+            sliderEnd,
+            trackStrokeWidth,
+            StrokeCap.Round
+        )
+        val sliderValueEnd = Offset(
+            sliderStart.x +
+                (sliderEnd.x - sliderStart.x) * activeRangeEnd,
+            center.y
+        )
+
+        val sliderValueStart = Offset(
+            sliderStart.x +
+                (sliderEnd.x - sliderStart.x) * activeRangeStart,
+            center.y
+        )
+
+        drawLine(
+            activeTrackColor,
+            sliderValueStart,
+            sliderValueEnd,
+            trackStrokeWidth,
+            StrokeCap.Round
+        )
+        tickFractions.groupBy {
+            it > activeRangeEnd ||
+                it < activeRangeStart
+        }.forEach { (outsideFraction, list) ->
+            drawPoints(
+                list.map {
+                    Offset(lerp(sliderStart, sliderEnd, it).x, center.y)
+                },
+                PointMode.Points,
+                (if (outsideFraction) inactiveTickColor else activeTickColor),
+                tickSize,
+                StrokeCap.Round
+            )
+        }
+    }
 }
 
 private fun snapValueToTick(
@@ -1159,37 +1290,72 @@
     }.progressSemantics(value, valueRange, steps)
 }
 
+@OptIn(ExperimentalMaterial3Api::class)
+private fun Modifier.sliderSemantics(
+    state: SliderState,
+    enabled: Boolean
+): Modifier {
+    val coerced = state.value.coerceIn(state.valueRange.start, state.valueRange.endInclusive)
+    return semantics {
+        if (!enabled) disabled()
+        setProgress(
+            action = { targetValue ->
+                var newValue = targetValue.coerceIn(
+                    state.valueRange.start,
+                    state.valueRange.endInclusive
+                )
+                val originalVal = newValue
+                val resolvedValue = if (state.steps > 0) {
+                    var distance: Float = newValue
+                    for (i in 0..state.steps + 1) {
+                        val stepValue = lerp(
+                            state.valueRange.start,
+                            state.valueRange.endInclusive,
+                            i.toFloat() / (state.steps + 1)
+                        )
+                        if (abs(stepValue - originalVal) <= distance) {
+                            distance = abs(stepValue - originalVal)
+                            newValue = stepValue
+                        }
+                    }
+                    newValue
+                } else {
+                    newValue
+                }
+
+                // This is to keep it consistent with AbsSeekbar.java: return false if no
+                // change from current.
+                if (resolvedValue == coerced) {
+                    false
+                } else {
+                    state.onValueChange(resolvedValue)
+                    state.onValueChangeFinished?.invoke()
+                    true
+                }
+            }
+        )
+    }.progressSemantics(state.value, state.valueRange, state.steps)
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
 private fun Modifier.sliderTapModifier(
-    draggableState: DraggableState,
+    state: SliderState,
     interactionSource: MutableInteractionSource,
-    maxPx: Int,
-    isRtl: Boolean,
-    rawOffset: State<Float>,
-    gestureEndAction: State<() -> Unit>,
-    pressOffset: MutableState<Float>,
     enabled: Boolean
 ) = composed(
     factory = {
         if (enabled) {
             val scope = rememberCoroutineScope()
-            pointerInput(draggableState, interactionSource, maxPx, isRtl) {
+            pointerInput(state.draggableState, interactionSource, state.totalWidth, state.isRtl) {
                 detectTapGestures(
-                    onPress = { pos ->
-                        val to = if (isRtl) maxPx - pos.x else pos.x
-                        pressOffset.value = to - rawOffset.value
-                        try {
-                            awaitRelease()
-                        } catch (_: GestureCancellationException) {
-                            pressOffset.value = 0f
-                        }
-                    },
+                    onPress = state.press,
                     onTap = {
                         scope.launch {
-                            draggableState.drag(MutatePriority.UserInput) {
+                            state.draggableState.drag(MutatePriority.UserInput) {
                                 // just trigger animation, press offset will be applied
                                 dragBy(0f)
                             }
-                            gestureEndAction.value.invoke()
+                            state.gestureEndAction()
                         }
                     }
                 )
@@ -1200,31 +1366,11 @@
     },
     inspectorInfo = debugInspectorInfo {
         name = "sliderTapModifier"
-        properties["draggableState"] = draggableState
+        properties["state"] = state
         properties["interactionSource"] = interactionSource
-        properties["maxPx"] = maxPx
-        properties["isRtl"] = isRtl
-        properties["rawOffset"] = rawOffset
-        properties["gestureEndAction"] = gestureEndAction
-        properties["pressOffset"] = pressOffset
         properties["enabled"] = enabled
     })
 
-private suspend fun animateToTarget(
-    draggableState: DraggableState,
-    current: Float,
-    target: Float,
-    velocity: Float
-) {
-    draggableState.drag {
-        var latestValue = current
-        Animatable(initialValue = current).animateTo(target, SliderToTickAnimation, velocity) {
-            dragBy(this.value - latestValue)
-            latestValue = this.value
-        }
-    }
-}
-
 private fun Modifier.rangeSliderPressDragModifier(
     startInteractionSource: MutableInteractionSource,
     endInteractionSource: MutableInteractionSource,
@@ -1428,7 +1574,7 @@
 
 private val SliderToTickAnimation = TweenSpec<Float>(durationMillis = 100)
 
-private class SliderDraggableState(
+internal class SliderDraggableState(
     val onDelta: (Float) -> Unit
 ) : DraggableState {
 
@@ -1505,4 +1651,119 @@
         result = 31 * result + tickFractions.contentHashCode()
         return result
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Class that holds information about [Slider]'s active range.
+ *
+ * @param initialValue [Float] that indicates the initial
+ * position of the thumb. If outside of [valueRange]
+ * provided, value will be coerced to this range.
+ * @param initialOnValueChange callback in which [value] should be updated.
+ * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
+ * between across the whole value range. If 0, range slider will behave as a continuous slider and
+ * allow to choose any value from the range specified. Must not be negative.
+ * @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
+ * shouldn't be used to update the range slider values (use [onValueChange] for that),
+ * but rather to know when the user has completed selecting a new value by ending a drag or a click.
+ * @param valueRange range of values that Slider values can take. [value] will be
+ * coerced to this range.
+ */
+@Stable
+@ExperimentalMaterial3Api
+class SliderState(
+    initialValue: Float = 0f,
+    initialOnValueChange: ((Float) -> Unit)? = null,
+    /*@IntRange(from = 0)*/
+    val steps: Int = 0,
+    val valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
+    var onValueChangeFinished: (() -> Unit)? = null
+) {
+    private var valueState by mutableStateOf(initialValue)
+
+    /**
+     * [Float] that indicates the current value that the thumb
+     * currently is in respect to the track.
+     */
+    var value: Float
+        set(newVal) {
+            val coercedValue = newVal.coerceIn(valueRange.start, valueRange.endInclusive)
+            val snappedValue = snapValueToTick(
+                coercedValue,
+                tickFractions,
+                valueRange.start,
+                valueRange.endInclusive
+            )
+            valueState = snappedValue
+        }
+        get() = valueState
+
+    /**
+     * callback in which value should be updated
+     */
+    internal var onValueChange: (Float) -> Unit = {
+        if (it != value) {
+            initialOnValueChange?.invoke(it) ?: defaultOnValueChange(it)
+        }
+    }
+
+    internal val tickFractions = stepsToTickFractions(steps)
+
+    private var thumbWidth by mutableStateOf(ThumbWidth.value)
+    internal var totalWidth by mutableStateOf(0)
+
+    internal var rawOffset by mutableStateOf(scaleToOffset(0f, 0f, value))
+    internal var pressOffset by mutableStateOf(0f)
+
+    internal var isRtl = false
+
+    internal val coercedValueAsFraction
+        get() = calcFraction(
+            valueRange.start,
+            valueRange.endInclusive,
+            value.coerceIn(valueRange.start, valueRange.endInclusive)
+        )
+
+    internal val draggableState =
+        SliderDraggableState {
+            val maxPx = max(totalWidth - thumbWidth / 2, 0f)
+            val minPx = min(thumbWidth / 2, maxPx)
+            rawOffset = (rawOffset + it + pressOffset)
+            pressOffset = 0f
+            val offsetInTrack = snapValueToTick(rawOffset, tickFractions, minPx, maxPx)
+            onValueChange(scaleToUserValue(minPx, maxPx, offsetInTrack))
+        }
+
+    internal val gestureEndAction = {
+        if (!draggableState.isDragging) {
+            // check isDragging in case the change is still in progress (touch -> drag case)
+            onValueChangeFinished?.invoke()
+        }
+    }
+
+    internal val press: suspend PressGestureScope.(Offset) -> Unit = { pos ->
+        val to = if (isRtl) totalWidth - pos.x else pos.x
+        pressOffset = to - rawOffset
+        try {
+            awaitRelease()
+        } catch (_: GestureCancellationException) {
+            pressOffset = 0f
+        }
+    }
+
+    internal fun updateDimensions(
+        newThumbWidth: Float,
+        newTotalWidth: Int
+    ) {
+        thumbWidth = newThumbWidth
+        totalWidth = newTotalWidth
+    }
+
+    private fun defaultOnValueChange(newVal: Float) { value = newVal }
+
+    private fun scaleToUserValue(minPx: Float, maxPx: Float, offset: Float) =
+        scale(minPx, maxPx, offset, valueRange.start, valueRange.endInclusive)
+
+    private fun scaleToOffset(minPx: Float, maxPx: Float, userValue: Float) =
+        scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
+}
diff --git a/compose/runtime/OWNERS b/compose/runtime/OWNERS
index 8983dbc..a9c5fa6 100644
--- a/compose/runtime/OWNERS
+++ b/compose/runtime/OWNERS
@@ -2,6 +2,7 @@
 jsproch@google.com
 chuckj@google.com
 lelandr@google.com
+anbailey@google.com
 
 # Per-file for Playground infra
 per-file settings.gradle = yboyar@google.com, dustinlam@google.com, rahulrav@google.com
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index f7317d7..37a15fa 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,87 +23,95 @@
     id("com.android.library")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-
-        api(libs.kotlinCoroutinesAndroid)
-
-        implementation("androidx.annotation:annotation:1.1.0")
-        implementation(libs.kotlinStdlib)
-
-        testImplementation(libs.kotlinTestJunit)
-        testImplementation(libs.junit)
-        testImplementation(libs.robolectric)
-        testImplementation(libs.kotlinCoroutinesTest)
-
-        androidTestImplementation(libs.kotlinTestJunit)
-        androidTestImplementation(libs.testExtJunit)
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-
-        lintChecks(projectOrArtifact(":compose:runtime:runtime-lint"))
-        lintPublish(projectOrArtifact(":compose:runtime:runtime-lint"))
-
-        samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
             }
-            jvmMain.dependencies {
-                implementation(libs.kotlinStdlib)
-                api(libs.kotlinCoroutinesCore)
-            }
-            androidMain {
-                dependencies {
-                    api(libs.kotlinCoroutinesAndroid)
-                    api("androidx.annotation:annotation:1.1.0")
-                }
-            }
+        }
 
-            commonTest.dependencies {
+        commonTest {
+            dependencies {
                 implementation kotlin("test")
                 implementation(libs.kotlinCoroutinesTest)
             }
+        }
 
-            androidAndroidTest {
+        jvmMain {
+            dependencies {
+                implementation(libs.kotlinStdlib)
+                api(libs.kotlinCoroutinesCore)
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api(libs.kotlinCoroutinesAndroid)
+                api("androidx.annotation:annotation:1.1.0")
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        nonEmulatorCommonTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        nonEmulatorJvmTest {
+            dependsOn(nonEmulatorCommonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testExtJunit)
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.truth)
+            }
+        }
+
+        androidTest {
+            dependsOn(jvmTest)
+            dependsOn(nonEmulatorJvmTest)
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
                 dependsOn(jvmTest)
-                dependencies {
-                    implementation(libs.testExtJunit)
-                    implementation(libs.testRules)
-                    implementation(libs.testRunner)
-                    implementation(libs.truth)
-                }
+                dependsOn(nonEmulatorJvmTest)
             }
         }
     }
-    dependencies {
-        samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
-    }
+}
+
+dependencies {
+    lintChecks(projectOrArtifact(":compose:runtime:runtime-lint"))
+    lintPublish(projectOrArtifact(":compose:runtime:runtime-lint"))
+    samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
 }
 
 android {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
index 0d74edd..4fa3504 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
@@ -27,9 +27,8 @@
 import androidx.compose.runtime.snapshots.newWritableRecord
 import androidx.compose.runtime.snapshots.sync
 import androidx.compose.runtime.snapshots.withCurrent
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
 import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlin.math.min
 
 /**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
index 1905768..cd26a4c 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
@@ -18,12 +18,11 @@
 @file:JvmMultifileClass
 package androidx.compose.runtime
 
+import kotlin.coroutines.CoroutineContext
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlin.coroutines.CoroutineContext
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmMultifileClass
 
 /**
  * Receiver scope for use with [produceState].
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
index 12250ab..3d7c842 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
@@ -19,17 +19,16 @@
 package androidx.compose.runtime
 
 import androidx.compose.runtime.snapshots.Snapshot
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.withContext
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmMultifileClass
 
 /**
  * Collects values from this [StateFlow] and represents its latest value via [State].
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt
index 69f2809..5b301a9 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt
@@ -18,11 +18,10 @@
 @file:JvmMultifileClass
 package androidx.compose.runtime
 
-import androidx.compose.runtime.snapshots.MutableSnapshot
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmMultifileClass
 import androidx.compose.runtime.internal.JvmDefaultWithCompatibility
+import androidx.compose.runtime.snapshots.MutableSnapshot
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 /**
  * A policy to control how the result of [mutableStateOf] report and merge changes to
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
index 7f1533b..d408eb3 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
@@ -27,9 +27,8 @@
 import androidx.compose.runtime.snapshots.overwritable
 import androidx.compose.runtime.snapshots.readable
 import androidx.compose.runtime.snapshots.withCurrent
-// Explicit imports for jvm annotations needed in common source sets.
-import kotlin.jvm.JvmName
 import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlin.reflect.KProperty
 
 /**
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/LatchTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/LatchTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/LatchTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/LatchTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RestartTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RestartTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RestartTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RestartTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Contact.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Point.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Point.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Point.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Point.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Report.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Report.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Report.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Report.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/View.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/View.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Views.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Views.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 1107548..2042050 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -16,8 +16,8 @@
   }
 
   public final class AndroidColorSpace_androidKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toFrameworkColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
   }
 
   public final class AndroidImageBitmap_androidKt {
@@ -1620,5 +1620,9 @@
     method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
   }
 
+  public final class PathParserKt {
+    method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+  }
+
 }
 
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
index 9a11ebe..cb69cd3 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -16,8 +16,8 @@
   }
 
   public final class AndroidColorSpace_androidKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toFrameworkColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
   }
 
   public final class AndroidImageBitmap_androidKt {
@@ -1623,5 +1623,9 @@
     method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
   }
 
+  public final class PathParserKt {
+    method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+  }
+
 }
 
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index 80bf1ea..032a956b 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -46,8 +46,8 @@
   }
 
   public final class AndroidColorSpace_androidKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toFrameworkColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
   }
 
   public final class AndroidImageBitmap_androidKt {
@@ -1679,5 +1679,9 @@
     method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
   }
 
+  public final class PathParserKt {
+    method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+  }
+
 }
 
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt
index 25fdfe8..ca5301a 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt
@@ -212,6 +212,34 @@
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     @Test
+    fun testUnknownColorSpaceNoTransform() {
+        val name = "MyCustomColorSpace"
+        val whitePoint = floatArrayOf(1.0f, 2.0f, 3.0f)
+        val primaries = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
+        colorSpaceTestHelper(
+            androidx.compose.ui.graphics.colorspace.Rgb(
+                name = name,
+                primaries = primaries,
+                WhitePoint(1.0f, 2.0f, 3.0f),
+                { 1.0 },
+                { 2.0 },
+                2f,
+                4f,
+            ),
+            ColorSpace.Rgb(
+                name,
+                primaries,
+                whitePoint,
+                { _ -> 1.0 },
+                { _ -> 2.0 },
+                2f,
+                4f,
+            )
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
     fun testUnknownColorspace2WhitePointValues() {
         val name = "MyCustomColorSpace"
         val whitePoint = floatArrayOf(1.0f, 2.0f)
@@ -251,7 +279,11 @@
                 composeColorSpace: androidx.compose.ui.graphics.colorspace.ColorSpace,
                 frameworkColorSpace: ColorSpace
             ) {
-                Assert.assertEquals(composeColorSpace, frameworkColorSpace.toComposeColorSpace())
+                val convertedColorSpace = frameworkColorSpace.toComposeColorSpace()
+                Assert.assertEquals(composeColorSpace, convertedColorSpace)
+
+                val frameworkConvertedColorSpace = convertedColorSpace.toAndroidColorSpace()
+                Assert.assertEquals(frameworkColorSpace, frameworkConvertedColorSpace)
             }
         }
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
index 66041c1..37086be 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.graphics
 
+import android.graphics.ColorSpace.get
 import android.os.Build
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
@@ -29,9 +30,9 @@
  * Convert the Compose [ColorSpace] into an Android framework [android.graphics.ColorSpace]
  */
 @RequiresApi(Build.VERSION_CODES.O)
-fun ColorSpace.toFrameworkColorSpace(): android.graphics.ColorSpace =
+fun ColorSpace.toAndroidColorSpace(): android.graphics.ColorSpace =
         with(ColorSpaceVerificationHelper) {
-            frameworkColorSpace()
+            androidColorSpace()
         }
 
 /**
@@ -49,28 +50,65 @@
     @DoNotInline
     @JvmStatic
     @RequiresApi(Build.VERSION_CODES.O)
-    fun ColorSpace.frameworkColorSpace(): android.graphics.ColorSpace {
-        val frameworkNamedSpace = when (this) {
-            ColorSpaces.Srgb -> android.graphics.ColorSpace.Named.SRGB
-            ColorSpaces.Aces -> android.graphics.ColorSpace.Named.ACES
-            ColorSpaces.Acescg -> android.graphics.ColorSpace.Named.ACESCG
-            ColorSpaces.AdobeRgb -> android.graphics.ColorSpace.Named.ADOBE_RGB
-            ColorSpaces.Bt2020 -> android.graphics.ColorSpace.Named.BT2020
-            ColorSpaces.Bt709 -> android.graphics.ColorSpace.Named.BT709
-            ColorSpaces.CieLab -> android.graphics.ColorSpace.Named.CIE_LAB
-            ColorSpaces.CieXyz -> android.graphics.ColorSpace.Named.CIE_XYZ
-            ColorSpaces.DciP3 -> android.graphics.ColorSpace.Named.DCI_P3
-            ColorSpaces.DisplayP3 -> android.graphics.ColorSpace.Named.DISPLAY_P3
-            ColorSpaces.ExtendedSrgb -> android.graphics.ColorSpace.Named.EXTENDED_SRGB
+    fun ColorSpace.androidColorSpace(): android.graphics.ColorSpace {
+        return when (this) {
+            ColorSpaces.Srgb -> get(android.graphics.ColorSpace.Named.SRGB)
+            ColorSpaces.Aces -> get(android.graphics.ColorSpace.Named.ACES)
+            ColorSpaces.Acescg -> get(android.graphics.ColorSpace.Named.ACESCG)
+            ColorSpaces.AdobeRgb -> get(android.graphics.ColorSpace.Named.ADOBE_RGB)
+            ColorSpaces.Bt2020 -> get(android.graphics.ColorSpace.Named.BT2020)
+            ColorSpaces.Bt709 -> get(android.graphics.ColorSpace.Named.BT709)
+            ColorSpaces.CieLab -> get(android.graphics.ColorSpace.Named.CIE_LAB)
+            ColorSpaces.CieXyz -> get(android.graphics.ColorSpace.Named.CIE_XYZ)
+            ColorSpaces.DciP3 -> get(android.graphics.ColorSpace.Named.DCI_P3)
+            ColorSpaces.DisplayP3 -> get(android.graphics.ColorSpace.Named.DISPLAY_P3)
+            ColorSpaces.ExtendedSrgb -> get(android.graphics.ColorSpace.Named.EXTENDED_SRGB)
             ColorSpaces.LinearExtendedSrgb ->
-                android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB
-            ColorSpaces.LinearSrgb -> android.graphics.ColorSpace.Named.LINEAR_SRGB
-            ColorSpaces.Ntsc1953 -> android.graphics.ColorSpace.Named.NTSC_1953
-            ColorSpaces.ProPhotoRgb -> android.graphics.ColorSpace.Named.PRO_PHOTO_RGB
-            ColorSpaces.SmpteC -> android.graphics.ColorSpace.Named.SMPTE_C
-            else -> android.graphics.ColorSpace.Named.SRGB
+                get(android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB)
+            ColorSpaces.LinearSrgb -> get(android.graphics.ColorSpace.Named.LINEAR_SRGB)
+            ColorSpaces.Ntsc1953 -> get(android.graphics.ColorSpace.Named.NTSC_1953)
+            ColorSpaces.ProPhotoRgb -> get(android.graphics.ColorSpace.Named.PRO_PHOTO_RGB)
+            ColorSpaces.SmpteC -> get(android.graphics.ColorSpace.Named.SMPTE_C)
+            else -> {
+                if (this is Rgb) {
+                    val whitePointArray = this.whitePoint.toXyz()
+                    val transferParams = this.transferParameters
+                    val androidTransferParams = if (transferParams != null) {
+                        android.graphics.ColorSpace.Rgb.TransferParameters(
+                            transferParams.a,
+                            transferParams.b,
+                            transferParams.c,
+                            transferParams.d,
+                            transferParams.e,
+                            transferParams.f,
+                            transferParams.gamma
+                        )
+                    } else {
+                        null
+                    }
+                    if (androidTransferParams != null) {
+                        android.graphics.ColorSpace.Rgb(
+                            this.name,
+                            this.primaries,
+                            whitePointArray,
+                            androidTransferParams
+                        )
+                    } else {
+                        android.graphics.ColorSpace.Rgb(
+                            this.name,
+                            this.primaries,
+                            whitePointArray,
+                            this.oetf,
+                            this.eotf,
+                            this.getMinValue(0),
+                            this.getMaxValue(0)
+                        )
+                    }
+                } else {
+                    get(android.graphics.ColorSpace.Named.SRGB)
+                }
+            }
         }
-        return android.graphics.ColorSpace.get(frameworkNamedSpace)
     }
 
     @DoNotInline
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
index 463d8f3..93d2f9c 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
@@ -201,7 +201,7 @@
             height,
             bitmapConfig.toBitmapConfig(),
             hasAlpha,
-            colorSpace.toFrameworkColorSpace()
+            colorSpace.toAndroidColorSpace()
         )
     }
 
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
index 66f1e24e..42cf68f 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
@@ -49,27 +49,15 @@
 internal val EmptyArray = FloatArray(0)
 
 class PathParser {
-    private data class PathPoint(var x: Float = 0.0f, var y: Float = 0.0f) {
-        fun reset() {
-            x = 0.0f
-            y = 0.0f
-        }
-    }
-
     private val nodes = mutableListOf<PathNode>()
 
+    private val floatResult = FloatResult()
+    private var nodeData = FloatArray(64)
+
     fun clear() {
         nodes.clear()
     }
 
-    private val currentPoint = PathPoint()
-    private val ctrlPoint = PathPoint()
-    private val segmentPoint = PathPoint()
-    private val reflectiveCtrlPoint = PathPoint()
-
-    private val floatResult = FloatResult()
-    private var nodeData = FloatArray(64)
-
     /**
      * Parses the path string to create a collection of PathNode instances with their corresponding
      * arguments
@@ -153,430 +141,412 @@
 
     fun toNodes(): List<PathNode> = nodes
 
-    fun toPath(target: Path = Path()): Path {
-        target.reset()
-        currentPoint.reset()
-        ctrlPoint.reset()
-        segmentPoint.reset()
-        reflectiveCtrlPoint.reset()
-
-        var previousNode: PathNode? = null
-        nodes.fastForEach { node ->
-            if (previousNode == null) previousNode = node
-            when (node) {
-                is Close -> close(target)
-                is RelativeMoveTo -> node.relativeMoveTo(target)
-                is MoveTo -> node.moveTo(target)
-                is RelativeLineTo -> node.relativeLineTo(target)
-                is LineTo -> node.lineTo(target)
-                is RelativeHorizontalTo -> node.relativeHorizontalTo(target)
-                is HorizontalTo -> node.horizontalTo(target)
-                is RelativeVerticalTo -> node.relativeVerticalTo(target)
-                is VerticalTo -> node.verticalTo(target)
-                is RelativeCurveTo -> node.relativeCurveTo(target)
-                is CurveTo -> node.curveTo(target)
-                is RelativeReflectiveCurveTo ->
-                    node.relativeReflectiveCurveTo(previousNode!!.isCurve, target)
-                is ReflectiveCurveTo -> node.reflectiveCurveTo(previousNode!!.isCurve, target)
-                is RelativeQuadTo -> node.relativeQuadTo(target)
-                is QuadTo -> node.quadTo(target)
-                is RelativeReflectiveQuadTo ->
-                    node.relativeReflectiveQuadTo(previousNode!!.isQuad, target)
-                is ReflectiveQuadTo -> node.reflectiveQuadTo(previousNode!!.isQuad, target)
-                is RelativeArcTo -> node.relativeArcTo(target)
-                is ArcTo -> node.arcTo(target)
-            }
-            previousNode = node
-        }
-        return target
-    }
-
-    private fun close(target: Path) {
-        currentPoint.x = segmentPoint.x
-        currentPoint.y = segmentPoint.y
-        ctrlPoint.x = segmentPoint.x
-        ctrlPoint.y = segmentPoint.y
-
-        target.close()
-        target.moveTo(currentPoint.x, currentPoint.y)
-    }
-
-    private fun RelativeMoveTo.relativeMoveTo(target: Path) {
-        currentPoint.x += dx
-        currentPoint.y += dy
-        target.relativeMoveTo(dx, dy)
-        segmentPoint.x = currentPoint.x
-        segmentPoint.y = currentPoint.y
-    }
-
-    private fun MoveTo.moveTo(target: Path) {
-        currentPoint.x = x
-        currentPoint.y = y
-        target.moveTo(x, y)
-        segmentPoint.x = currentPoint.x
-        segmentPoint.y = currentPoint.y
-    }
-
-    private fun RelativeLineTo.relativeLineTo(target: Path) {
-        target.relativeLineTo(dx, dy)
-        currentPoint.x += dx
-        currentPoint.y += dy
-    }
-
-    private fun LineTo.lineTo(target: Path) {
-        target.lineTo(x, y)
-        currentPoint.x = x
-        currentPoint.y = y
-    }
-
-    private fun RelativeHorizontalTo.relativeHorizontalTo(target: Path) {
-        target.relativeLineTo(dx, 0.0f)
-        currentPoint.x += dx
-    }
-
-    private fun HorizontalTo.horizontalTo(target: Path) {
-        target.lineTo(x, currentPoint.y)
-        currentPoint.x = x
-    }
-
-    private fun RelativeVerticalTo.relativeVerticalTo(target: Path) {
-        target.relativeLineTo(0.0f, dy)
-        currentPoint.y += dy
-    }
-
-    private fun VerticalTo.verticalTo(target: Path) {
-        target.lineTo(currentPoint.x, y)
-        currentPoint.y = y
-    }
-
-    private fun RelativeCurveTo.relativeCurveTo(target: Path) {
-        target.relativeCubicTo(
-            dx1, dy1,
-            dx2, dy2,
-            dx3, dy3
-        )
-        ctrlPoint.x = currentPoint.x + dx2
-        ctrlPoint.y = currentPoint.y + dy2
-        currentPoint.x += dx3
-        currentPoint.y += dy3
-    }
-
-    private fun CurveTo.curveTo(target: Path) {
-        target.cubicTo(
-            x1, y1,
-            x2, y2,
-            x3, y3
-        )
-        ctrlPoint.x = x2
-        ctrlPoint.y = y2
-        currentPoint.x = x3
-        currentPoint.y = y3
-    }
-
-    private fun RelativeReflectiveCurveTo.relativeReflectiveCurveTo(
-        prevIsCurve: Boolean,
-        target: Path
-    ) {
-        if (prevIsCurve) {
-            reflectiveCtrlPoint.x = currentPoint.x - ctrlPoint.x
-            reflectiveCtrlPoint.y = currentPoint.y - ctrlPoint.y
-        } else {
-            reflectiveCtrlPoint.reset()
-        }
-
-        target.relativeCubicTo(
-            reflectiveCtrlPoint.x, reflectiveCtrlPoint.y,
-            dx1, dy1,
-            dx2, dy2
-        )
-        ctrlPoint.x = currentPoint.x + dx1
-        ctrlPoint.y = currentPoint.y + dy1
-        currentPoint.x += dx2
-        currentPoint.y += dy2
-    }
-
-    private fun ReflectiveCurveTo.reflectiveCurveTo(prevIsCurve: Boolean, target: Path) {
-        if (prevIsCurve) {
-            reflectiveCtrlPoint.x = 2 * currentPoint.x - ctrlPoint.x
-            reflectiveCtrlPoint.y = 2 * currentPoint.y - ctrlPoint.y
-        } else {
-            reflectiveCtrlPoint.x = currentPoint.x
-            reflectiveCtrlPoint.y = currentPoint.y
-        }
-
-        target.cubicTo(
-            reflectiveCtrlPoint.x, reflectiveCtrlPoint.y,
-            x1, y1, x2, y2
-        )
-        ctrlPoint.x = x1
-        ctrlPoint.y = y1
-        currentPoint.x = x2
-        currentPoint.y = y2
-    }
-
-    private fun RelativeQuadTo.relativeQuadTo(target: Path) {
-        target.relativeQuadraticBezierTo(dx1, dy1, dx2, dy2)
-        ctrlPoint.x = currentPoint.x + dx1
-        ctrlPoint.y = currentPoint.y + dy1
-        currentPoint.x += dx2
-        currentPoint.y += dy2
-    }
-
-    private fun QuadTo.quadTo(target: Path) {
-        target.quadraticBezierTo(x1, y1, x2, y2)
-        ctrlPoint.x = x1
-        ctrlPoint.y = y1
-        currentPoint.x = x2
-        currentPoint.y = y2
-    }
-
-    private fun RelativeReflectiveQuadTo.relativeReflectiveQuadTo(
-        prevIsQuad: Boolean,
-        target: Path
-    ) {
-        if (prevIsQuad) {
-            reflectiveCtrlPoint.x = currentPoint.x - ctrlPoint.x
-            reflectiveCtrlPoint.y = currentPoint.y - ctrlPoint.y
-        } else {
-            reflectiveCtrlPoint.reset()
-        }
-
-        target.relativeQuadraticBezierTo(
-            reflectiveCtrlPoint.x,
-            reflectiveCtrlPoint.y, dx, dy
-        )
-        ctrlPoint.x = currentPoint.x + reflectiveCtrlPoint.x
-        ctrlPoint.y = currentPoint.y + reflectiveCtrlPoint.y
-        currentPoint.x += dx
-        currentPoint.y += dy
-    }
-
-    private fun ReflectiveQuadTo.reflectiveQuadTo(prevIsQuad: Boolean, target: Path) {
-        if (prevIsQuad) {
-            reflectiveCtrlPoint.x = 2 * currentPoint.x - ctrlPoint.x
-            reflectiveCtrlPoint.y = 2 * currentPoint.y - ctrlPoint.y
-        } else {
-            reflectiveCtrlPoint.x = currentPoint.x
-            reflectiveCtrlPoint.y = currentPoint.y
-        }
-        target.quadraticBezierTo(
-            reflectiveCtrlPoint.x,
-            reflectiveCtrlPoint.y, x, y
-        )
-        ctrlPoint.x = reflectiveCtrlPoint.x
-        ctrlPoint.y = reflectiveCtrlPoint.y
-        currentPoint.x = x
-        currentPoint.y = y
-    }
-
-    private fun RelativeArcTo.relativeArcTo(target: Path) {
-        val arcStartX = arcStartDx + currentPoint.x
-        val arcStartY = arcStartDy + currentPoint.y
-
-        drawArc(
-            target,
-            currentPoint.x.toDouble(),
-            currentPoint.y.toDouble(),
-            arcStartX.toDouble(),
-            arcStartY.toDouble(),
-            horizontalEllipseRadius.toDouble(),
-            verticalEllipseRadius.toDouble(),
-            theta.toDouble(),
-            isMoreThanHalf,
-            isPositiveArc
-        )
-        currentPoint.x = arcStartX
-        currentPoint.y = arcStartY
-
-        ctrlPoint.x = currentPoint.x
-        ctrlPoint.y = currentPoint.y
-    }
-
-    private fun ArcTo.arcTo(target: Path) {
-        drawArc(
-            target,
-            currentPoint.x.toDouble(),
-            currentPoint.y.toDouble(),
-            arcStartX.toDouble(),
-            arcStartY.toDouble(),
-            horizontalEllipseRadius.toDouble(),
-            verticalEllipseRadius.toDouble(),
-            theta.toDouble(),
-            isMoreThanHalf,
-            isPositiveArc
-        )
-
-        currentPoint.x = arcStartX
-        currentPoint.y = arcStartY
-
-        ctrlPoint.x = currentPoint.x
-        ctrlPoint.y = currentPoint.y
-    }
-
-    private fun drawArc(
-        p: Path,
-        x0: Double,
-        y0: Double,
-        x1: Double,
-        y1: Double,
-        a: Double,
-        b: Double,
-        theta: Double,
-        isMoreThanHalf: Boolean,
-        isPositiveArc: Boolean
-    ) {
-
-        /* Convert rotation angle from degrees to radians */
-        val thetaD = theta.toRadians()
-        /* Pre-compute rotation matrix entries */
-        val cosTheta = cos(thetaD)
-        val sinTheta = sin(thetaD)
-        /* Transform (x0, y0) and (x1, y1) into unit space */
-        /* using (inverse) rotation, followed by (inverse) scale */
-        val x0p = (x0 * cosTheta + y0 * sinTheta) / a
-        val y0p = (-x0 * sinTheta + y0 * cosTheta) / b
-        val x1p = (x1 * cosTheta + y1 * sinTheta) / a
-        val y1p = (-x1 * sinTheta + y1 * cosTheta) / b
-
-        /* Compute differences and averages */
-        val dx = x0p - x1p
-        val dy = y0p - y1p
-        val xm = (x0p + x1p) / 2
-        val ym = (y0p + y1p) / 2
-        /* Solve for intersecting unit circles */
-        val dsq = dx * dx + dy * dy
-        if (dsq == 0.0) {
-            return /* Points are coincident */
-        }
-        val disc = 1.0 / dsq - 1.0 / 4.0
-        if (disc < 0.0) {
-            val adjust = (sqrt(dsq) / 1.99999).toFloat()
-            drawArc(
-                p, x0, y0, x1, y1, a * adjust,
-                b * adjust, theta, isMoreThanHalf, isPositiveArc
-            )
-            return /* Points are too far apart */
-        }
-        val s = sqrt(disc)
-        val sdx = s * dx
-        val sdy = s * dy
-        var cx: Double
-        var cy: Double
-        if (isMoreThanHalf == isPositiveArc) {
-            cx = xm - sdy
-            cy = ym + sdx
-        } else {
-            cx = xm + sdy
-            cy = ym - sdx
-        }
-
-        val eta0 = atan2(y0p - cy, x0p - cx)
-
-        val eta1 = atan2(y1p - cy, x1p - cx)
-
-        var sweep = eta1 - eta0
-        if (isPositiveArc != (sweep >= 0)) {
-            if (sweep > 0) {
-                sweep -= 2 * PI
-            } else {
-                sweep += 2 * PI
-            }
-        }
-
-        cx *= a
-        cy *= b
-        val tcx = cx
-        cx = cx * cosTheta - cy * sinTheta
-        cy = tcx * sinTheta + cy * cosTheta
-
-        arcToBezier(
-            p, cx, cy, a, b, x0, y0, thetaD,
-            eta0, sweep
-        )
-    }
-
-    /**
-     * Converts an arc to cubic Bezier segments and records them in p.
-     *
-     * @param p The target for the cubic Bezier segments
-     * @param cx The x coordinate center of the ellipse
-     * @param cy The y coordinate center of the ellipse
-     * @param a The radius of the ellipse in the horizontal direction
-     * @param b The radius of the ellipse in the vertical direction
-     * @param e1x E(eta1) x coordinate of the starting point of the arc
-     * @param e1y E(eta2) y coordinate of the starting point of the arc
-     * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
-     * @param start The start angle of the arc on the ellipse
-     * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
-     */
-    private fun arcToBezier(
-        p: Path,
-        cx: Double,
-        cy: Double,
-        a: Double,
-        b: Double,
-        e1x: Double,
-        e1y: Double,
-        theta: Double,
-        start: Double,
-        sweep: Double
-    ) {
-        var eta1x = e1x
-        var eta1y = e1y
-        // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
-        // and http://www.spaceroots.org/documents/ellipse/node22.html
-
-        // Maximum of 45 degrees per cubic Bezier segment
-        val numSegments = ceil(abs(sweep * 4 / PI)).toInt()
-
-        var eta1 = start
-        val cosTheta = cos(theta)
-        val sinTheta = sin(theta)
-        val cosEta1 = cos(eta1)
-        val sinEta1 = sin(eta1)
-        var ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1)
-        var ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1)
-
-        val anglePerSegment = sweep / numSegments
-        for (i in 0 until numSegments) {
-            val eta2 = eta1 + anglePerSegment
-            val sinEta2 = sin(eta2)
-            val cosEta2 = cos(eta2)
-            val e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2)
-            val e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2)
-            val ep2x = (-a * cosTheta * sinEta2) - (b * sinTheta * cosEta2)
-            val ep2y = (-a * sinTheta * sinEta2) + (b * cosTheta * cosEta2)
-            val tanDiff2 = tan((eta2 - eta1) / 2)
-            val alpha = sin(eta2 - eta1) * (sqrt(4 + 3.0 * tanDiff2 * tanDiff2) - 1) / 3
-            val q1x = eta1x + alpha * ep1x
-            val q1y = eta1y + alpha * ep1y
-            val q2x = e2x - alpha * ep2x
-            val q2y = e2y - alpha * ep2y
-
-            // TODO (njawad) figure out if this is still necessary?
-            // Adding this no-op call to workaround a proguard related issue.
-            // p.relativeLineTo(0.0, 0.0)
-
-            p.cubicTo(
-                q1x.toFloat(),
-                q1y.toFloat(),
-                q2x.toFloat(),
-                q2y.toFloat(),
-                e2x.toFloat(),
-                e2y.toFloat()
-            )
-            eta1 = eta2
-            eta1x = e2x
-            eta1y = e2y
-            ep1x = ep2x
-            ep1y = ep2y
-        }
-    }
+    fun toPath(target: Path = Path()) = nodes.toPath(target)
 
     @Suppress("NOTHING_TO_INLINE")
     private inline fun addNodes(cmd: Char, args: FloatArray, count: Int) {
         cmd.addPathNodes(nodes, args, count)
     }
-
-    private fun Double.toRadians(): Double = this / 180 * PI
 }
+
+/**
+ * Converts this list of [PathNode] into a [Path] by adding the appropriate
+ * commands to the [target] path. If [target] is not specified, a new
+ * [Path] instance is created. This method returns [target] or the newly
+ * created [Path].
+ */
+fun List<PathNode>.toPath(target: Path = Path()): Path {
+    // Rewind unsets the filltype so reset it here
+    val fillType = target.fillType
+    target.rewind()
+    target.fillType = fillType
+
+    var currentX = 0.0f
+    var currentY = 0.0f
+    var ctrlX = 0.0f
+    var ctrlY = 0.0f
+    var segmentX = 0.0f
+    var segmentY = 0.0f
+    var reflectiveCtrlX: Float
+    var reflectiveCtrlY: Float
+
+    var previousNode = if (isEmpty()) Close else this[0]
+    fastForEach { node ->
+        when (node) {
+            is Close -> {
+                currentX = segmentX
+                currentY = segmentY
+                ctrlX = segmentX
+                ctrlY = segmentY
+                target.close()
+                target.moveTo(currentX, currentY)
+            }
+
+            is RelativeMoveTo -> {
+                currentX += node.dx
+                currentY += node.dy
+                target.relativeMoveTo(node.dx, node.dy)
+                segmentX = currentX
+                segmentY = currentY
+            }
+
+            is MoveTo -> {
+                currentX = node.x
+                currentY = node.y
+                target.moveTo(node.x, node.y)
+                segmentX = currentX
+                segmentY = currentY
+            }
+
+            is RelativeLineTo -> {
+                target.relativeLineTo(node.dx, node.dy)
+                currentX += node.dx
+                currentY += node.dy
+            }
+
+            is LineTo -> {
+                target.lineTo(node.x, node.y)
+                currentX = node.x
+                currentY = node.y
+            }
+
+            is RelativeHorizontalTo -> {
+                target.relativeLineTo(node.dx, 0.0f)
+                currentX += node.dx
+            }
+
+            is HorizontalTo -> {
+                target.lineTo(node.x, currentY)
+                currentX = node.x
+            }
+
+            is RelativeVerticalTo -> {
+                target.relativeLineTo(0.0f, node.dy)
+                currentY += node.dy
+            }
+
+            is VerticalTo -> {
+                target.lineTo(currentX, node.y)
+                currentY = node.y
+            }
+
+            is RelativeCurveTo -> {
+                target.relativeCubicTo(
+                    node.dx1, node.dy1,
+                    node.dx2, node.dy2,
+                    node.dx3, node.dy3
+                )
+                ctrlX = currentX + node.dx2
+                ctrlY = currentY + node.dy2
+                currentX += node.dx3
+                currentY += node.dy3
+            }
+
+            is CurveTo -> {
+                target.cubicTo(
+                    node.x1, node.y1,
+                    node.x2, node.y2,
+                    node.x3, node.y3
+                )
+                ctrlX = node.x2
+                ctrlY = node.y2
+                currentX = node.x3
+                currentY = node.y3
+            }
+
+            is RelativeReflectiveCurveTo -> {
+                if (previousNode.isCurve) {
+                    reflectiveCtrlX = currentX - ctrlX
+                    reflectiveCtrlY = currentY - ctrlY
+                } else {
+                    reflectiveCtrlX = 0.0f
+                    reflectiveCtrlY = 0.0f
+                }
+                target.relativeCubicTo(
+                    reflectiveCtrlX, reflectiveCtrlY,
+                    node.dx1, node.dy1,
+                    node.dx2, node.dy2
+                )
+                ctrlX = currentX + node.dx1
+                ctrlY = currentY + node.dy1
+                currentX += node.dx2
+                currentY += node.dy2
+            }
+
+            is ReflectiveCurveTo -> {
+                if (previousNode.isCurve) {
+                    reflectiveCtrlX = 2 * currentX - ctrlX
+                    reflectiveCtrlY = 2 * currentY - ctrlY
+                } else {
+                    reflectiveCtrlX = currentX
+                    reflectiveCtrlY = currentY
+                }
+                target.cubicTo(
+                    reflectiveCtrlX, reflectiveCtrlY,
+                    node.x1, node.y1, node.x2, node.y2
+                )
+                ctrlX = node.x1
+                ctrlY = node.y1
+                currentX = node.x2
+                currentY = node.y2
+            }
+
+            is RelativeQuadTo -> {
+                target.relativeQuadraticBezierTo(node.dx1, node.dy1, node.dx2, node.dy2)
+                ctrlX = currentX + node.dx1
+                ctrlY = currentY + node.dy1
+                currentX += node.dx2
+                currentY += node.dy2
+            }
+
+            is QuadTo -> {
+                target.quadraticBezierTo(node.x1, node.y1, node.x2, node.y2)
+                ctrlX = node.x1
+                ctrlY = node.y1
+                currentX = node.x2
+                currentY = node.y2
+            }
+
+            is RelativeReflectiveQuadTo -> {
+                if (previousNode.isQuad) {
+                    reflectiveCtrlX = currentX - ctrlX
+                    reflectiveCtrlY = currentY - ctrlY
+                } else {
+                    reflectiveCtrlX = 0.0f
+                    reflectiveCtrlY = 0.0f
+                }
+                target.relativeQuadraticBezierTo(
+                    reflectiveCtrlX,
+                    reflectiveCtrlY, node.dx, node.dy
+                )
+                ctrlX = currentX + reflectiveCtrlX
+                ctrlY = currentY + reflectiveCtrlY
+                currentX += node.dx
+                currentY += node.dy
+            }
+
+            is ReflectiveQuadTo -> {
+                if (previousNode.isQuad) {
+                    reflectiveCtrlX = 2 * currentX - ctrlX
+                    reflectiveCtrlY = 2 * currentY - ctrlY
+                } else {
+                    reflectiveCtrlX = currentX
+                    reflectiveCtrlY = currentY
+                }
+                target.quadraticBezierTo(
+                    reflectiveCtrlX,
+                    reflectiveCtrlY, node.x, node.y
+                )
+                ctrlX = reflectiveCtrlX
+                ctrlY = reflectiveCtrlY
+                currentX = node.x
+                currentY = node.y
+            }
+
+            is RelativeArcTo -> {
+                val arcStartX = node.arcStartDx + currentX
+                val arcStartY = node.arcStartDy + currentY
+                drawArc(
+                    target,
+                    currentX.toDouble(),
+                    currentY.toDouble(),
+                    arcStartX.toDouble(),
+                    arcStartY.toDouble(),
+                    node.horizontalEllipseRadius.toDouble(),
+                    node.verticalEllipseRadius.toDouble(),
+                    node.theta.toDouble(),
+                    node.isMoreThanHalf,
+                    node.isPositiveArc
+                )
+                currentX = arcStartX
+                currentY = arcStartY
+                ctrlX = currentX
+                ctrlY = currentY
+            }
+
+            is ArcTo -> {
+                drawArc(
+                    target,
+                    currentX.toDouble(),
+                    currentY.toDouble(),
+                    node.arcStartX.toDouble(),
+                    node.arcStartY.toDouble(),
+                    node.horizontalEllipseRadius.toDouble(),
+                    node.verticalEllipseRadius.toDouble(),
+                    node.theta.toDouble(),
+                    node.isMoreThanHalf,
+                    node.isPositiveArc
+                )
+                currentX = node.arcStartX
+                currentY = node.arcStartY
+                ctrlX = currentX
+                ctrlY = currentY
+            }
+        }
+        previousNode = node
+    }
+    return target
+}
+
+private fun drawArc(
+    p: Path,
+    x0: Double,
+    y0: Double,
+    x1: Double,
+    y1: Double,
+    a: Double,
+    b: Double,
+    theta: Double,
+    isMoreThanHalf: Boolean,
+    isPositiveArc: Boolean
+) {
+
+    /* Convert rotation angle from degrees to radians */
+    val thetaD = theta.toRadians()
+    /* Pre-compute rotation matrix entries */
+    val cosTheta = cos(thetaD)
+    val sinTheta = sin(thetaD)
+    /* Transform (x0, y0) and (x1, y1) into unit space */
+    /* using (inverse) rotation, followed by (inverse) scale */
+    val x0p = (x0 * cosTheta + y0 * sinTheta) / a
+    val y0p = (-x0 * sinTheta + y0 * cosTheta) / b
+    val x1p = (x1 * cosTheta + y1 * sinTheta) / a
+    val y1p = (-x1 * sinTheta + y1 * cosTheta) / b
+
+    /* Compute differences and averages */
+    val dx = x0p - x1p
+    val dy = y0p - y1p
+    val xm = (x0p + x1p) / 2
+    val ym = (y0p + y1p) / 2
+    /* Solve for intersecting unit circles */
+    val dsq = dx * dx + dy * dy
+    if (dsq == 0.0) {
+        return /* Points are coincident */
+    }
+    val disc = 1.0 / dsq - 1.0 / 4.0
+    if (disc < 0.0) {
+        val adjust = (sqrt(dsq) / 1.99999).toFloat()
+        drawArc(
+            p, x0, y0, x1, y1, a * adjust,
+            b * adjust, theta, isMoreThanHalf, isPositiveArc
+        )
+        return /* Points are too far apart */
+    }
+    val s = sqrt(disc)
+    val sdx = s * dx
+    val sdy = s * dy
+    var cx: Double
+    var cy: Double
+    if (isMoreThanHalf == isPositiveArc) {
+        cx = xm - sdy
+        cy = ym + sdx
+    } else {
+        cx = xm + sdy
+        cy = ym - sdx
+    }
+
+    val eta0 = atan2(y0p - cy, x0p - cx)
+
+    val eta1 = atan2(y1p - cy, x1p - cx)
+
+    var sweep = eta1 - eta0
+    if (isPositiveArc != (sweep >= 0)) {
+        if (sweep > 0) {
+            sweep -= 2 * PI
+        } else {
+            sweep += 2 * PI
+        }
+    }
+
+    cx *= a
+    cy *= b
+    val tcx = cx
+    cx = cx * cosTheta - cy * sinTheta
+    cy = tcx * sinTheta + cy * cosTheta
+
+    arcToBezier(
+        p, cx, cy, a, b, x0, y0, thetaD,
+        eta0, sweep
+    )
+}
+
+/**
+ * Converts an arc to cubic Bezier segments and records them in p.
+ *
+ * @param p The target for the cubic Bezier segments
+ * @param cx The x coordinate center of the ellipse
+ * @param cy The y coordinate center of the ellipse
+ * @param a The radius of the ellipse in the horizontal direction
+ * @param b The radius of the ellipse in the vertical direction
+ * @param e1x E(eta1) x coordinate of the starting point of the arc
+ * @param e1y E(eta2) y coordinate of the starting point of the arc
+ * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+ * @param start The start angle of the arc on the ellipse
+ * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+ */
+private fun arcToBezier(
+    p: Path,
+    cx: Double,
+    cy: Double,
+    a: Double,
+    b: Double,
+    e1x: Double,
+    e1y: Double,
+    theta: Double,
+    start: Double,
+    sweep: Double
+) {
+    var eta1x = e1x
+    var eta1y = e1y
+    // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+    // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+    // Maximum of 45 degrees per cubic Bezier segment
+    val numSegments = ceil(abs(sweep * 4 / PI)).toInt()
+
+    var eta1 = start
+    val cosTheta = cos(theta)
+    val sinTheta = sin(theta)
+    val cosEta1 = cos(eta1)
+    val sinEta1 = sin(eta1)
+    var ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1)
+    var ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1)
+
+    val anglePerSegment = sweep / numSegments
+    for (i in 0 until numSegments) {
+        val eta2 = eta1 + anglePerSegment
+        val sinEta2 = sin(eta2)
+        val cosEta2 = cos(eta2)
+        val e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2)
+        val e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2)
+        val ep2x = (-a * cosTheta * sinEta2) - (b * sinTheta * cosEta2)
+        val ep2y = (-a * sinTheta * sinEta2) + (b * cosTheta * cosEta2)
+        val tanDiff2 = tan((eta2 - eta1) / 2)
+        val alpha = sin(eta2 - eta1) * (sqrt(4 + 3.0 * tanDiff2 * tanDiff2) - 1) / 3
+        val q1x = eta1x + alpha * ep1x
+        val q1y = eta1y + alpha * ep1y
+        val q2x = e2x - alpha * ep2x
+        val q2y = e2y - alpha * ep2y
+
+        // TODO (njawad) figure out if this is still necessary?
+        // Adding this no-op call to workaround a proguard related issue.
+        // p.relativeLineTo(0.0, 0.0)
+
+        p.cubicTo(
+            q1x.toFloat(),
+            q1y.toFloat(),
+            q2x.toFloat(),
+            q2y.toFloat(),
+            e2x.toFloat(),
+            e2y.toFloat()
+        )
+        eta1 = eta2
+        eta1x = e2x
+        eta1y = e2y
+        ep1x = ep2x
+        ep1y = ep2y
+    }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+private inline fun Double.toRadians(): Double = this / 180 * PI
diff --git a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
index 23c7e7a..0e0f06e 100644
--- a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
+++ b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
@@ -32,7 +32,6 @@
 import org.jetbrains.skia.ClipMode as SkClipMode
 import org.jetbrains.skia.RRect as SkRRect
 import org.jetbrains.skia.Rect as SkRect
-// Using skiko use as it has versions for all mpp platforms
 import org.jetbrains.skia.impl.use
 
 actual typealias NativeCanvas = org.jetbrains.skia.Canvas
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
index 9b17b00..18e4696 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
@@ -607,7 +607,7 @@
 
     fun enqueueRotaryScrollVertically(verticalScrollPixels: Float) {
         // TODO(b/214437966): figure out if ongoing scroll events need to be cancelled.
-        rotaryInputState.enqueueRotaryScrollHorizontally(verticalScrollPixels)
+        rotaryInputState.enqueueRotaryScrollVertically(verticalScrollPixels)
     }
 
     private fun MouseInputState.enterHover() {
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index 1f0ad8e..09ac6ba 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -509,7 +509,7 @@
   public final class TextRangeKt {
     method public static long TextRange(int start, int end);
     method public static long TextRange(int index);
-    method public static long constrain(long, int minimumValue, int maximumValue);
+    method public static long coerceIn(long, int minimumValue, int maximumValue);
     method public static String substring(CharSequence, long range);
   }
 
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index 509ae13..4299328 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -522,7 +522,7 @@
   public final class TextRangeKt {
     method public static long TextRange(int start, int end);
     method public static long TextRange(int index);
-    method public static long constrain(long, int minimumValue, int maximumValue);
+    method public static long coerceIn(long, int minimumValue, int maximumValue);
     method public static String substring(CharSequence, long range);
   }
 
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 1f0ad8e..09ac6ba 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -509,7 +509,7 @@
   public final class TextRangeKt {
     method public static long TextRange(int start, int end);
     method public static long TextRange(int index);
-    method public static long constrain(long, int minimumValue, int maximumValue);
+    method public static long coerceIn(long, int minimumValue, int maximumValue);
     method public static String substring(CharSequence, long range);
   }
 
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
index af0ce0e3..b847c51 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
@@ -473,7 +473,7 @@
     @Test
     fun textMeasurerDraw_isConstrainedTo_canvasSizeByDefault() {
         val measurer = textMeasurer()
-        // constrain the width, height is ignored
+        // coerceIn the width, height is ignored
         val textLayoutResult = measurer.measure(
             text = longText,
             style = TextStyle(
@@ -499,7 +499,7 @@
     @Test
     fun textMeasurerDraw_usesCanvasDensity_ByDefault() {
         val measurer = textMeasurer()
-        // constrain the width, height is ignored
+        // coerceIn the width, height is ignored
         val textLayoutResult = measurer.measure(
             text = longText,
             style = TextStyle(
@@ -528,7 +528,7 @@
     @Test
     fun drawTextClipsTheContent_ifOverflowIsClip() {
         val measurer = textMeasurer()
-        // constrain the width, height is ignored
+        // coerceIn the width, height is ignored
         val textLayoutResult = measurer.measure(
             text = longText,
             style = TextStyle(
@@ -588,7 +588,7 @@
     @Test
     fun drawTextDoesNotClipTheContent_ifOverflowIsVisible() {
         val measurer = textMeasurer()
-        // constrain the width, height is ignored
+        // coerceIn the width, height is ignored
         val textLayoutResult = measurer.measure(
             text = longText,
             style = TextStyle(
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
index 35b572e..cc2ed1d 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
@@ -50,8 +50,8 @@
 import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_JUSTIFICATION_MODE
 import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_LINESPACING_MULTIPLIER
 import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_NONE
-import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_NORMAL
-import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_NORMAL_FAST
+import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_FULL
+import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_FULL_FAST
 import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_LINE_BREAK_STYLE
 import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_LINE_BREAK_WORD_STYLE
 import androidx.compose.ui.text.android.LayoutCompat.JUSTIFICATION_MODE_INTER_WORD
@@ -562,9 +562,9 @@
 @OptIn(InternalPlatformTextApi::class)
 private fun toLayoutHyphenationFrequency(hyphens: Hyphens?): Int = when (hyphens) {
     Hyphens.Auto -> if (Build.VERSION.SDK_INT <= 32) {
-        HYPHENATION_FREQUENCY_NORMAL
+        HYPHENATION_FREQUENCY_FULL
     } else {
-        HYPHENATION_FREQUENCY_NORMAL_FAST
+        HYPHENATION_FREQUENCY_FULL_FAST
     }
     Hyphens.None -> HYPHENATION_FREQUENCY_NONE
     else -> DEFAULT_HYPHENATION_FREQUENCY
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt
index fbad0c0..2fbf0a7 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt
@@ -108,7 +108,7 @@
  * @param minimumValue the minimum value that [TextRange.start] or [TextRange.end] can be.
  * @param maximumValue the exclusive maximum value that [TextRange.start] or [TextRange.end] can be.
  */
-fun TextRange.constrain(minimumValue: Int, maximumValue: Int): TextRange {
+fun TextRange.coerceIn(minimumValue: Int, maximumValue: Int): TextRange {
     val newStart = start.coerceIn(minimumValue, maximumValue)
     val newEnd = end.coerceIn(minimumValue, maximumValue)
     if (newStart != start || newEnd != end) {
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
index d8dbaaf..ed074ba 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
@@ -22,7 +22,7 @@
 import androidx.compose.ui.text.AnnotatedStringSaver
 import androidx.compose.ui.text.Saver
 import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.constrain
+import androidx.compose.ui.text.coerceIn
 import androidx.compose.ui.text.restore
 import androidx.compose.ui.text.save
 import kotlin.math.max
@@ -85,7 +85,7 @@
      * The selection range. If the selection is collapsed, it represents cursor
      * location. When selection range is out of bounds, it is constrained with the text length.
      */
-    val selection: TextRange = selection.constrain(0, text.length)
+    val selection: TextRange = selection.coerceIn(0, text.length)
 
     /**
      * Composition range created by  IME. If null, there is no composition range.
@@ -99,7 +99,7 @@
      * composition by setting the value to null. Applying a composition will accept the changes
      * that were still being composed by IME.
      */
-    val composition: TextRange? = composition?.constrain(0, text.length)
+    val composition: TextRange? = composition?.coerceIn(0, text.length)
 
     /**
      * Returns a copy of the TextFieldValue.
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt
index 36fd1bc..9c47e02 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt
@@ -139,23 +139,23 @@
 
     @Test
     fun constrain_updates_start_end_if_required() {
-        assertThat(TextRange(0, 4).constrain(1, 3)).isEqualTo(TextRange(1, 3))
+        assertThat(TextRange(0, 4).coerceIn(1, 3)).isEqualTo(TextRange(1, 3))
     }
 
     @Test
     fun constrain_with_collapsed_min_max_returns_collapsed_values() {
-        assertThat(TextRange(1, 2).constrain(2, 2)).isEqualTo(TextRange(2, 2))
-        assertThat(TextRange(2, 3).constrain(2, 2)).isEqualTo(TextRange(2, 2))
+        assertThat(TextRange(1, 2).coerceIn(2, 2)).isEqualTo(TextRange(2, 2))
+        assertThat(TextRange(2, 3).coerceIn(2, 2)).isEqualTo(TextRange(2, 2))
     }
 
     @Test
     fun constrain_min_max_greater_than_TextRange_values() {
-        assertThat(TextRange(0, 4).constrain(5, 6)).isEqualTo(TextRange(5, 5))
+        assertThat(TextRange(0, 4).coerceIn(5, 6)).isEqualTo(TextRange(5, 5))
     }
 
     @Test
     fun constrain_min_smaller_than_TextRange_values() {
-        assertThat(TextRange(5, 6).constrain(0, 4)).isEqualTo(TextRange(4, 4))
+        assertThat(TextRange(5, 6).coerceIn(0, 4)).isEqualTo(TextRange(4, 4))
     }
 
     @Test
diff --git a/compose/ui/ui-tooling-preview/api/current.ignore b/compose/ui/ui-tooling-preview/api/current.ignore
new file mode 100644
index 0000000..71dfdb2
--- /dev/null
+++ b/compose/ui/ui-tooling-preview/api/current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#DESKTOP:
+    Field androidx.compose.ui.tooling.preview.Devices.DESKTOP has changed value from spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420 to spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#FOLDABLE:
+    Field androidx.compose.ui.tooling.preview.Devices.FOLDABLE has changed value from spec:shape=Normal,width=673,height=841,unit=dp,dpi=480 to spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#TABLET:
+    Field androidx.compose.ui.tooling.preview.Devices.TABLET has changed value from spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420 to spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240
diff --git a/compose/ui/ui-tooling-preview/api/current.txt b/compose/ui/ui-tooling-preview/api/current.txt
index a5363d9..9d09eb2 100644
--- a/compose/ui/ui-tooling-preview/api/current.txt
+++ b/compose/ui/ui-tooling-preview/api/current.txt
@@ -4,8 +4,8 @@
   public final class Devices {
     field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
     field public static final String DEFAULT = "";
-    field public static final String DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
-    field public static final String FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480";
+    field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+    field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
     field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
     field public static final String NEXUS_10 = "name:Nexus 10";
     field public static final String NEXUS_5 = "id:Nexus 5";
@@ -27,7 +27,7 @@
     field public static final String PIXEL_4_XL = "id:pixel_4_xl";
     field public static final String PIXEL_C = "id:pixel_c";
     field public static final String PIXEL_XL = "id:pixel_xl";
-    field public static final String TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420";
+    field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
     field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
     field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
     field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
diff --git a/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt b/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt
index a5363d9..9d09eb2 100644
--- a/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt
@@ -4,8 +4,8 @@
   public final class Devices {
     field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
     field public static final String DEFAULT = "";
-    field public static final String DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
-    field public static final String FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480";
+    field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+    field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
     field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
     field public static final String NEXUS_10 = "name:Nexus 10";
     field public static final String NEXUS_5 = "id:Nexus 5";
@@ -27,7 +27,7 @@
     field public static final String PIXEL_4_XL = "id:pixel_4_xl";
     field public static final String PIXEL_C = "id:pixel_c";
     field public static final String PIXEL_XL = "id:pixel_xl";
-    field public static final String TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420";
+    field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
     field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
     field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
     field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
diff --git a/compose/ui/ui-tooling-preview/api/restricted_current.ignore b/compose/ui/ui-tooling-preview/api/restricted_current.ignore
new file mode 100644
index 0000000..71dfdb2
--- /dev/null
+++ b/compose/ui/ui-tooling-preview/api/restricted_current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#DESKTOP:
+    Field androidx.compose.ui.tooling.preview.Devices.DESKTOP has changed value from spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420 to spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#FOLDABLE:
+    Field androidx.compose.ui.tooling.preview.Devices.FOLDABLE has changed value from spec:shape=Normal,width=673,height=841,unit=dp,dpi=480 to spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#TABLET:
+    Field androidx.compose.ui.tooling.preview.Devices.TABLET has changed value from spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420 to spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240
diff --git a/compose/ui/ui-tooling-preview/api/restricted_current.txt b/compose/ui/ui-tooling-preview/api/restricted_current.txt
index a5363d9..9d09eb2 100644
--- a/compose/ui/ui-tooling-preview/api/restricted_current.txt
+++ b/compose/ui/ui-tooling-preview/api/restricted_current.txt
@@ -4,8 +4,8 @@
   public final class Devices {
     field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
     field public static final String DEFAULT = "";
-    field public static final String DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
-    field public static final String FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480";
+    field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+    field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
     field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
     field public static final String NEXUS_10 = "name:Nexus 10";
     field public static final String NEXUS_5 = "id:Nexus 5";
@@ -27,7 +27,7 @@
     field public static final String PIXEL_4_XL = "id:pixel_4_xl";
     field public static final String PIXEL_C = "id:pixel_c";
     field public static final String PIXEL_XL = "id:pixel_xl";
-    field public static final String TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420";
+    field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
     field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
     field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
     field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
diff --git a/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
index c6405e4..57fa29b 100644
--- a/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
+++ b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
@@ -53,9 +53,11 @@
 
     // Reference devices
     const val PHONE = "spec:id=reference_phone,shape=Normal,width=411,height=891,unit=dp,dpi=420"
-    const val FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480"
-    const val TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420"
-    const val DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420"
+    const val FOLDABLE =
+        "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420"
+    const val TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240"
+    const val DESKTOP =
+        "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160"
 
     // TV devices (not adding 4K since it will be very heavy for preview)
     const val TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420"
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt
index e8aed9a..6a22a71 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt
@@ -21,7 +21,9 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.BottomAppBar
@@ -29,12 +31,14 @@
 import androidx.compose.material.FabPosition
 import androidx.compose.material.FloatingActionButton
 import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
 import androidx.compose.material.Text
 import androidx.compose.material.TopAppBar
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Face
 import androidx.compose.material.rememberDrawerState
 import androidx.compose.material.rememberScaffoldState
 import androidx.compose.runtime.Composable
@@ -42,6 +46,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.isTraversalGroup
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.traversalIndex
@@ -350,3 +355,47 @@
         }
     }
 }
+
+@Preview
+@Composable
+fun IconsInScaffoldWithListDemo() {
+    Scaffold(
+        topBar = {
+            Row(
+                horizontalArrangement = Arrangement.SpaceEvenly
+            ) {
+                IconButton(onClick = { }) {
+                    Icon(Icons.Default.Face, contentDescription = "Face 1")
+                }
+                // Setting `clearAndSetSemantics` below means that Face 2 will not be sorted nor
+                // will be read by TalkBack. The final traversal order should go from Face 1 to
+                // Face 3 to the LazyColumn content.
+                IconButton(
+                    onClick = { },
+                    modifier = Modifier.clearAndSetSemantics { }
+                ) {
+                    Icon(Icons.Default.Face, contentDescription = "Face 2")
+                }
+                IconButton(onClick = { }) {
+                    Icon(Icons.Default.Face, contentDescription = "Face 3")
+                }
+            }
+        },
+        content = { innerPadding ->
+            LazyColumn(
+                contentPadding = innerPadding,
+                verticalArrangement = Arrangement.spacedBy(8.dp)
+            ) {
+                val list = (0..75).map { it.toString() }
+                items(count = list.size) {
+                    Text(
+                        text = list[it],
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(horizontal = 16.dp)
+                    )
+                }
+            }
+        }
+    )
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index e2ebcd9..5d49476 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -79,6 +79,7 @@
 import androidx.compose.material.TopAppBar
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Face
 import androidx.compose.material.icons.filled.MoreVert
 import androidx.compose.material.rememberDrawerState
 import androidx.compose.material.rememberScaffoldState
@@ -141,6 +142,7 @@
 import androidx.compose.ui.test.assertValueEquals
 import androidx.compose.ui.test.isEnabled
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
@@ -1261,6 +1263,119 @@
         assertThat(contentTraverseBefore).isLessThan(bottomAppBarNode.id)
     }
 
+    @Test
+    fun testSortedAccessibilityNodeInfo_clearSemantics() {
+        val content1 = "Face 1"
+        val content2 = "Face 2"
+        val content3 = "Face 3"
+        val contentText = "Content"
+        container.setContent {
+            Scaffold(
+                topBar = {
+                    Row(
+                        horizontalArrangement = Arrangement.SpaceEvenly
+                    ) {
+                        IconButton(onClick = { }) {
+                            Icon(Icons.Default.Face, contentDescription = content1)
+                        }
+                        IconButton(
+                            onClick = { },
+                            modifier = Modifier.clearAndSetSemantics { }
+                        ) {
+                            Icon(Icons.Default.Face, contentDescription = content2)
+                        }
+                        IconButton(onClick = { }) {
+                            Icon(Icons.Default.Face, contentDescription = content3)
+                        }
+                    }
+                },
+                content = { padding -> Text(contentText, modifier = Modifier.padding(padding)) }
+            )
+        }
+        val faceNode1 = rule.onNodeWithContentDescription(content1).fetchSemanticsNode()
+        val faceNode3 = rule.onNodeWithContentDescription(content3).fetchSemanticsNode()
+        val contentNode = rule.onNodeWithText(contentText).fetchSemanticsNode()
+
+        val ANI1 = provider.createAccessibilityNodeInfo(faceNode1.id)
+        val ANI3 = provider.createAccessibilityNodeInfo(faceNode3.id)
+
+        val traverseBefore1 = ANI1?.extras?.getInt(EXTRA_DATA_TEST_TRAVERSALBEFORE_VAL)
+        val traverseBefore3 = ANI3?.extras?.getInt(EXTRA_DATA_TEST_TRAVERSALBEFORE_VAL)
+
+        // On screen we have three faces in a top app bar, and then a content node:
+        //
+        //     Face1       Face2      Face3
+        //               Content
+        //
+
+        // Since `clearAndSetSemantics` is set on Face2, it should not generate any semantics node.
+        rule.onNodeWithTag(content2).assertDoesNotExist()
+
+        // The traversal order for the elements on screen should then be Face1 -> Face3 -> content.
+        assertEquals(traverseBefore1, faceNode3.id)
+        assertEquals(traverseBefore3, contentNode.id)
+    }
+
+    @Test
+    fun testSortedAccessibilityNodeInfo_zOcclusion() {
+        val parentBox1Tag = "ParentForOverlappedChildren"
+        val childOneTag = "OverlappedChildOne"
+        val childTwoTag = "OverlappedChildTwo"
+        val childThreeTag = "ChildThree"
+
+        container.setContent {
+            Column {
+                Box(Modifier.testTag(parentBox1Tag)) {
+                    with(LocalDensity.current) {
+                        BasicText(
+                            "Child One",
+                            Modifier
+                                // A child with larger [zIndex] will be drawn on top of all the
+                                // children with smaller [zIndex]. So child 1 covers child 2.
+                                .zIndex(1f)
+                                .testTag(childOneTag)
+                                .requiredSize(50.toDp())
+                        )
+                        BasicText(
+                            "Child Two",
+                            Modifier
+                                .testTag(childTwoTag)
+                                .requiredSize(50.toDp())
+                        )
+                    }
+                }
+                Box {
+                    BasicText(
+                        "Child Three",
+                        Modifier
+                            .testTag(childThreeTag)
+                    )
+                }
+            }
+        }
+
+        val parentBox1Node = rule.onNodeWithTag(parentBox1Tag).fetchSemanticsNode()
+        val childOneNode = rule.onNodeWithTag(
+            childOneTag, useUnmergedTree = true).fetchSemanticsNode()
+        val childTwoNode = rule.onNodeWithTag(
+            childTwoTag, useUnmergedTree = true).fetchSemanticsNode()
+        val childThreeNode = rule.onNodeWithTag(
+            childThreeTag, useUnmergedTree = true).fetchSemanticsNode()
+
+        val ANI1 = provider.createAccessibilityNodeInfo(childOneNode.id)
+        val traverseBefore1 = ANI1?.extras?.getInt(EXTRA_DATA_TEST_TRAVERSALBEFORE_VAL)
+
+        // Since child 2 is completely covered, it should not generate any ANI. The first box
+        // parent should only have one child (child 1).
+        assertEquals(
+            1, provider.createAccessibilityNodeInfo(parentBox1Node.id)!!.childCount)
+        assertNull(provider.createAccessibilityNodeInfo(childTwoNode.id))
+
+        // The traversal order for the elements on screen should then be child 1 -> child 3,
+        // completely skipping over child 2.
+        assertEquals(traverseBefore1, childThreeNode.id)
+    }
+
     @Composable
     fun ScrollColumn(testTag: String) {
         var counter = 0
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 909c4a5..ad8183c 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
@@ -694,9 +694,9 @@
 
         fun depthFirstSearch(currNode: SemanticsNode) {
             // We only want to add children that are either traversalGroups or are
-            // screen reader focusable.
-            if (currNode.isTraversalGroup == true ||
-                isScreenReaderFocusable(currNode)) {
+            // screen reader focusable. The child must also be in the current pruned semantics tree.
+            if ((currNode.isTraversalGroup == true || isScreenReaderFocusable(currNode)) &&
+                currNode.id in currentSemanticsNodes.keys) {
                 geometryList.add(currNode)
             }
             if (currNode.isTraversalGroup == true) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
index 7e68ef8..562f991 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
@@ -145,11 +145,7 @@
     }
 
     fun DrawScope.draw(alpha: Float, colorFilter: ColorFilter?) {
-        val targetColorFilter = if (colorFilter != null) {
-            colorFilter
-        } else {
-            intrinsicColorFilter
-        }
+        val targetColorFilter = colorFilter ?: intrinsicColorFilter
         // If the content of the vector has changed, or we are drawing a different size
         // update the cached image to ensure we are scaling the vector appropriately
         if (isDirty || previousDrawSize != size) {
@@ -293,17 +289,18 @@
 
     private val pathMeasure: PathMeasure by lazy(LazyThreadSafetyMode.NONE) { PathMeasure() }
 
-    private val parser = PathParser()
-
     private fun updatePath() {
-        parser.clear()
-        path.reset()
-        parser.addPathNodes(pathData).toPath(path)
+        // The call below resets the path
+        pathData.toPath(path)
         updateRenderPath()
     }
 
     private fun updateRenderPath() {
-        renderPath.reset()
+        // Rewind unsets the filltype so reset it here
+        val fillType = renderPath.fillType
+        renderPath.rewind()
+        renderPath.fillType = fillType
+
         if (trimPathStart == DefaultTrimPathStart && trimPathEnd == DefaultTrimPathEnd) {
             renderPath.addPath(path)
         } else {
@@ -366,7 +363,6 @@
     private var isClipPathDirty = true
 
     private var clipPath: Path? = null
-    private var parser: PathParser? = null
 
     override var invalidateListener: (() -> Unit)? = null
         set(value) {
@@ -378,23 +374,14 @@
 
     private fun updateClipPath() {
         if (willClipPath) {
-            var targetParser = parser
-            if (targetParser == null) {
-                targetParser = PathParser()
-                parser = targetParser
-            } else {
-                targetParser.clear()
-            }
-
             var targetClip = clipPath
             if (targetClip == null) {
                 targetClip = Path()
                 clipPath = targetClip
-            } else {
-                targetClip.reset()
             }
 
-            targetParser.addPathNodes(clipPathData).toPath(targetClip)
+            // toPath() will reset the path we send
+            clipPathData.toPath(targetClip)
         }
     }
 
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 4545228..569d1ac 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -30,14 +30,14 @@
     docs("androidx.asynclayoutinflater:asynclayoutinflater:1.1.0-alpha01")
     docs("androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01")
     docs("androidx.autofill:autofill:1.2.0-beta01")
-    docs("androidx.benchmark:benchmark-common:1.2.0-alpha13")
-    docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha13")
-    docs("androidx.benchmark:benchmark-macro:1.2.0-alpha13")
-    docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha13")
+    docs("androidx.benchmark:benchmark-common:1.2.0-alpha14")
+    docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha14")
+    docs("androidx.benchmark:benchmark-macro:1.2.0-alpha14")
+    docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha14")
     docs("androidx.biometric:biometric:1.2.0-alpha05")
     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")
+    docs("androidx.browser:browser:1.6.0-alpha01")
     docs("androidx.camera:camera-camera2:1.3.0-alpha06")
     docs("androidx.camera:camera-core:1.3.0-alpha06")
     docs("androidx.camera:camera-extensions:1.3.0-alpha06")
@@ -252,18 +252,18 @@
     docs("androidx.navigation:navigation-testing:2.6.0-beta01")
     docs("androidx.navigation:navigation-ui:2.6.0-beta01")
     docs("androidx.navigation:navigation-ui-ktx:2.6.0-beta01")
-    docs("androidx.paging:paging-common:3.2.0-alpha04")
-    docs("androidx.paging:paging-common-ktx:3.2.0-alpha04")
-    docs("androidx.paging:paging-compose:1.0.0-alpha18")
-    samples("androidx.paging:paging-compose-samples:3.0.0-alpha08")
-    docs("androidx.paging:paging-guava:3.2.0-alpha04")
-    docs("androidx.paging:paging-runtime:3.2.0-alpha04")
-    docs("androidx.paging:paging-runtime-ktx:3.2.0-alpha04")
-    docs("androidx.paging:paging-rxjava2:3.2.0-alpha04")
-    docs("androidx.paging:paging-rxjava2-ktx:3.2.0-alpha04")
-    docs("androidx.paging:paging-rxjava3:3.2.0-alpha04")
-    samples("androidx.paging:paging-samples:3.2.0-alpha04")
-    docs("androidx.paging:paging-testing:3.2.0-alpha04")
+    docs("androidx.paging:paging-common:3.2.0-alpha05")
+    docs("androidx.paging:paging-common-ktx:3.2.0-alpha05")
+    docs("androidx.paging:paging-compose:1.0.0-alpha19")
+    samples("androidx.paging:paging-compose-samples:1.0.0-alpha19")
+    docs("androidx.paging:paging-guava:3.2.0-alpha05")
+    docs("androidx.paging:paging-runtime:3.2.0-alpha05")
+    docs("androidx.paging:paging-runtime-ktx:3.2.0-alpha05")
+    docs("androidx.paging:paging-rxjava2:3.2.0-alpha05")
+    docs("androidx.paging:paging-rxjava2-ktx:3.2.0-alpha05")
+    docs("androidx.paging:paging-rxjava3:3.2.0-alpha05")
+    samples("androidx.paging:paging-samples:3.2.0-alpha05")
+    docs("androidx.paging:paging-testing:3.2.0-alpha05")
     docs("androidx.palette:palette:1.0.0")
     docs("androidx.palette:palette-ktx:1.0.0")
     docs("androidx.percentlayout:percentlayout:1.0.1")
@@ -339,10 +339,10 @@
     docs("androidx.test.services:storage:1.5.0-alpha01")
     docs("androidx.test.uiautomator:uiautomator:2.3.0-alpha03")
     docs("androidx.textclassifier:textclassifier:1.0.0-alpha04")
-    docs("androidx.tracing:tracing:1.2.0-beta03")
-    docs("androidx.tracing:tracing-ktx:1.2.0-beta03")
-    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha14")
-    docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha14")
+    docs("androidx.tracing:tracing:1.2.0-beta04")
+    docs("androidx.tracing:tracing-ktx:1.2.0-beta04")
+    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha15")
+    docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha15")
     docs("androidx.transition:transition:1.4.1")
     docs("androidx.transition:transition-ktx:1.4.1")
     docs("androidx.tv:tv-foundation:1.0.0-alpha06")
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle b/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
index d2fff66..52f133f 100644
--- a/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
+++ b/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
@@ -34,7 +34,7 @@
 dependencies {
     implementation(libs.kotlinStdlib)
     implementation(libs.constraintLayout, { transitive = true })
-    implementation(project(":arch:core:core-runtime"))
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation(project(":appcompat:appcompat"))
     implementation(project(":profileinstaller:profileinstaller"))
     implementation(libs.material)
diff --git a/fragment/CHANGELOG.md b/fragment/CHANGELOG.md
new file mode 100644
index 0000000..55e45de
--- /dev/null
+++ b/fragment/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Log for changes in the Fragment library
+#
+# `Added`: for new features
+# `Changed`: for changes in existing functionality
+# `Deprecated`: for soon to be removed functionality
+# `Removed`: for now removed feature
+# `Fixed`: for any bug fixes
+# `Security`: in case of vulnerabilities
+
+# Unreleased
+
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
index a4ccfdc..6a3e51f 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
@@ -63,8 +63,10 @@
             StrictMode.VmPolicy.Builder()
                 .detectAll()
                 .penaltyListener(executor) {
+                    Log.e("StrictModeTest", "Logging violation:")
+                    Log.e("StrictModeTest", "$it")
+                    Log.e("StrictModeTest", "Stack trace: ${it.stackTrace}", it.cause)
                     fail("Received violation: $it")
-                    Log.e("WIDGET", "$it")
                 }.build()
         )
     }
@@ -166,4 +168,4 @@
         Truth.assertThat(CallbackTest.latch.await(5, TimeUnit.SECONDS)).isTrue()
         Truth.assertThat(CallbackTest.received.get()).containsExactly(1, 2)
     }
-}
+}
\ No newline at end of file
diff --git a/graphics/graphics-shapes/api/current.txt b/graphics/graphics-shapes/api/current.txt
index e7f488c..e4a534d 100644
--- a/graphics/graphics-shapes/api/current.txt
+++ b/graphics/graphics-shapes/api/current.txt
@@ -82,7 +82,7 @@
 
   public final class RoundedPolygon {
     ctor public RoundedPolygon(java.util.List<? extends android.graphics.PointF> vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF? center);
-    ctor public RoundedPolygon(int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+    ctor public RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
     ctor public RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
     method public android.graphics.RectF getBounds();
     method public android.graphics.PointF getCenter();
@@ -103,9 +103,11 @@
   }
 
   public final class ShapesKt {
-    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius, optional android.graphics.PointF center);
-    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
     method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+    method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
diff --git a/graphics/graphics-shapes/api/public_plus_experimental_current.txt b/graphics/graphics-shapes/api/public_plus_experimental_current.txt
index e7f488c..e4a534d 100644
--- a/graphics/graphics-shapes/api/public_plus_experimental_current.txt
+++ b/graphics/graphics-shapes/api/public_plus_experimental_current.txt
@@ -82,7 +82,7 @@
 
   public final class RoundedPolygon {
     ctor public RoundedPolygon(java.util.List<? extends android.graphics.PointF> vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF? center);
-    ctor public RoundedPolygon(int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+    ctor public RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
     ctor public RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
     method public android.graphics.RectF getBounds();
     method public android.graphics.PointF getCenter();
@@ -103,9 +103,11 @@
   }
 
   public final class ShapesKt {
-    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius, optional android.graphics.PointF center);
-    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
     method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+    method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
diff --git a/graphics/graphics-shapes/api/restricted_current.txt b/graphics/graphics-shapes/api/restricted_current.txt
index e7f488c..e4a534d 100644
--- a/graphics/graphics-shapes/api/restricted_current.txt
+++ b/graphics/graphics-shapes/api/restricted_current.txt
@@ -82,7 +82,7 @@
 
   public final class RoundedPolygon {
     ctor public RoundedPolygon(java.util.List<? extends android.graphics.PointF> vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF? center);
-    ctor public RoundedPolygon(int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+    ctor public RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
     ctor public RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
     method public android.graphics.RectF getBounds();
     method public android.graphics.PointF getCenter();
@@ -103,9 +103,11 @@
   }
 
   public final class ShapesKt {
-    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius, optional android.graphics.PointF center);
-    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+    method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
     method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+    method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
     method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt
index 918f919..eabbc8d 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt
@@ -20,6 +20,7 @@
 import androidx.core.graphics.plus
 import androidx.core.graphics.times
 import androidx.test.filters.SmallTest
+import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Test
 
@@ -31,6 +32,10 @@
 
     @Test
     fun numVertsConstructorTest() {
+        Assert.assertThrows(IllegalArgumentException::class.java) {
+            RoundedPolygon(2)
+        }
+
         val square = RoundedPolygon(4)
         var min = PointF(-1f, -1f)
         var max = PointF(1f, 1f)
@@ -58,6 +63,11 @@
         val p1 = PointF(0f, 1f)
         val p2 = PointF(-1f, 0f)
         val p3 = PointF(0f, -1f)
+
+        Assert.assertThrows(IllegalArgumentException::class.java) {
+            RoundedPolygon(listOf(p0, p1))
+        }
+
         val manualSquare = RoundedPolygon(listOf(p0, p1, p2, p3))
         var min = PointF(-1f, -1f)
         var max = PointF(1f, 1f)
@@ -109,4 +119,107 @@
         assertEqualish(0.5f, lowerEdge.p3.x)
         assertEqualish(0.0f, lowerEdge.p3.y)
     }
-}
\ No newline at end of file
+
+    /*
+     * In the following tests, we check how much was cut for the top left (vertex 0) and bottom
+     * left corner (vertex 3).
+     * In particular, both vertex are competing for space in the left side.
+     *
+     *   Vertex 0            Vertex 1
+     *      *---------------------*
+     *      |                     |
+     *      *---------------------*
+     *   Vertex 3            Vertex 2
+     */
+    private val points = 20
+
+    @Test
+    fun unevenSmoothingTest() {
+        // Vertex 3 has the default 0.5 radius, 0 smoothing.
+        // Vertex 0 has 0.4 radius, and smoothing varying from 0 to 1.
+        repeat(points + 1) {
+            val smooth = it.toFloat() / points
+            doUnevenSmoothTest(
+                CornerRounding(0.4f, smooth),
+                expectedV0SX = 0.4f * (1 + smooth),
+                expectedV0SY = (0.4f * (1 + smooth)).coerceAtMost(0.5f),
+                expectedV3SY = 0.5f,
+            )
+        }
+    }
+
+    @Test
+    fun unevenSmoothingTest2() {
+        // Vertex 3 has 0.2f radius and 0.2f smoothing, so it takes at most 0.4f
+        // Vertex 0 has 0.4f radius and smoothing varies from 0 to 1, when it reaches 0.5 it starts
+        // competing with vertex 3 for space.
+        repeat(points + 1) {
+            val smooth = it.toFloat() / points
+
+            val smoothWantedV0 = 0.4f * smooth
+            val smoothWantedV3 = 0.2f
+
+            // There is 0.4f room for smoothing
+            val factor = (0.4f / (smoothWantedV0 + smoothWantedV3)).coerceAtMost(1f)
+            doUnevenSmoothTest(
+                CornerRounding(0.4f, smooth),
+                expectedV0SX = 0.4f * (1 + smooth),
+                expectedV0SY = 0.4f + factor * smoothWantedV0,
+                expectedV3SY = 0.2f + factor * smoothWantedV3,
+                rounding3 = CornerRounding(0.2f, 1f)
+            )
+        }
+    }
+
+    @Test
+    fun unevenSmoothingTest3() {
+        // Vertex 3 has 0.6f radius.
+        // Vertex 0 has 0.4f radius and smoothing varies from 0 to 1. There is no room for smoothing
+        // on the segment between these vertices, but vertex 0 can still have smoothing on the top
+        // side.
+        repeat(points + 1) {
+            val smooth = it.toFloat() / points
+
+            doUnevenSmoothTest(
+                CornerRounding(0.4f, smooth),
+                expectedV0SX = 0.4f * (1 + smooth),
+                expectedV0SY = 0.4f,
+                expectedV3SY = 0.6f,
+                rounding3 = CornerRounding(0.6f)
+            )
+        }
+    }
+
+    private fun doUnevenSmoothTest(
+        // Corner rounding parameter for vertex 0 (top left)
+        rounding0: CornerRounding,
+        expectedV0SX: Float, // Expected total cut from vertex 0 towards vertex 1
+        expectedV0SY: Float, // Expected total cut from vertex 0 towards vertex 3
+        expectedV3SY: Float, // Expected total cut from vertex 3 towards vertex 0
+        // Corner rounding parameter for vertex 3 (bottom left)
+        rounding3: CornerRounding = CornerRounding(0.5f)
+    ) {
+        val p0 = PointF(0f, 0f)
+        val p1 = PointF(5f, 0f)
+        val p2 = PointF(5f, 1f)
+        val p3 = PointF(0f, 1f)
+
+        val pvRounding = listOf(
+            rounding0,
+            CornerRounding.Unrounded,
+            CornerRounding.Unrounded,
+            rounding3,
+        )
+        val polygon = RoundedPolygon(
+            vertices = listOf(p0, p1, p2, p3),
+            perVertexRounding = pvRounding
+        )
+        val (e01, _, _, e30) = polygon.features.filterIsInstance<RoundedPolygon.Edge>()
+        val msg = "r0 = ${show(rounding0)}, r3 = ${show(rounding3)}"
+        assertEqualish(expectedV0SX, e01.cubics.first().p0.x, msg)
+        assertEqualish(expectedV0SY, e30.cubics.first().p3.y, msg)
+        assertEqualish(expectedV3SY, 1f - e30.cubics.first().p0.y, msg)
+    }
+
+    private fun show(cr: CornerRounding) = "(r=${cr.radius}, s=${cr.smoothing})"
+}
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt
index 4a115d7..abd569c 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.filters.SmallTest
 import kotlin.AssertionError
 import kotlin.math.sqrt
+import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertThrows
 import org.junit.Test
@@ -90,9 +91,19 @@
 
     @Test
     fun circleTest() {
+        Assert.assertThrows(IllegalArgumentException::class.java) {
+            RoundedPolygon.circle(2)
+        }
+
         val circle = RoundedPolygon.circle()
         assertCircleShape(circle.toCubicShape())
 
+        val simpleCircle = RoundedPolygon.circle(3)
+        assertCircleShape(simpleCircle.toCubicShape())
+
+        val complexCircle = RoundedPolygon.circle(20)
+        assertCircleShape(complexCircle.toCubicShape())
+
         val bigCircle = RoundedPolygon.circle(radius = 3f)
         assertCircleShape(bigCircle.toCubicShape(), radius = 3f)
 
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt
index d447fdf..31d0440 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt
@@ -45,8 +45,8 @@
     assertTrue(actual.y <= expected.y + Epsilon)
 }
 
-fun assertEqualish(expected: Float, actual: Float) {
-    assertEquals(expected, actual, Epsilon)
+fun assertEqualish(expected: Float, actual: Float, message: String? = null) {
+    assertEquals(message ?: "", expected, actual, Epsilon)
 }
 
 fun assertInBounds(shape: CubicShape, minPoint: PointF, maxPoint: PointF) {
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt
index 6d872cf..90f1cfa 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt
@@ -81,7 +81,7 @@
     /**
      * Transforms (scales, rotates, and translates) the shape by the given matrix.
      * Note that this operation alters the points in the shape directly; the original
-     * points are not retained, nor is the matrix itself. This calling this function
+     * points are not retained, nor is the matrix itself. Thus calling this function
      * twice with the same matrix will composite the effect. For example, a matrix which
      * scales by 2 will scale the shape by 2. Calling transform twice with that matrix
      * will have the effect os scaling the shape size by 4.
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
index 17c1fdd..520b787 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
@@ -22,6 +22,7 @@
 import android.graphics.Path
 import android.graphics.PointF
 import android.graphics.RectF
+import androidx.annotation.IntRange
 import androidx.core.graphics.div
 import androidx.core.graphics.minus
 import androidx.core.graphics.plus
@@ -80,8 +81,8 @@
      * Constructs a RoundedPolygon object from a given list of vertices, with optional
      * corner-rounding parameters for all corners or per-corner.
      *
-     * A RoundedPolygon without any rounding parameters is equivalent to a [RoundedPolygon] constructed
-     * with the same [vertices] and [center].
+     * A RoundedPolygon without any rounding parameters is equivalent to a [RoundedPolygon]
+     * constructed with the same [vertices] and [center].
      *
      * @param vertices The list of vertices in this polygon. This should be an ordered list
      * (with the outline of the shape going from each vertex to the next in order of this
@@ -99,6 +100,7 @@
      *
      * @throws IllegalArgumentException If [perVertexRounding] is not null, it must be
      * the same size as the [vertices] list.
+     * @throws IllegalArgumentException [vertices] must have a size of at least three.
      */
     constructor(
         vertices: List<PointF>,
@@ -113,7 +115,11 @@
     /**
      * This constructor takes the number of vertices in the resulting polygon. These vertices are
      * positioned on a virtual circle around a given center with each vertex positioned [radius]
-     * distance from that center, equally spaced (with equal angles between them).
+     * distance from that center, equally spaced (with equal angles between them). If no radius
+     * is supplied, the shape will be created with a default radius of 1, resulting in a shape
+     * whose vertices lie on a unit circle, with width/height of 2. That default polygon will
+     * probably need to be rescaled using [transform] into the appropriate size for the UI in
+     * which it will be drawn.
      *
      * The [rounding] and [perVertexRounding] parameters are optional. If not supplied, the result
      * will be a regular polygon with straight edges and unrounded corners.
@@ -135,9 +141,10 @@
      *
      * @throws IllegalArgumentException If [perVertexRounding] is not null, it must have
      * [numVertices] elements.
+     * @throws IllegalArgumentException [numVertices] must be at least 3.
      */
     constructor(
-        numVertices: Int,
+        @IntRange(from = 3) numVertices: Int,
         radius: Float = 1f,
         center: PointF = PointF(0f, 0f),
         rounding: CornerRounding = CornerRounding.Unrounded,
@@ -189,6 +196,9 @@
         rounding: CornerRounding = CornerRounding.Unrounded,
         perVertexRounding: List<CornerRounding>? = null
     ) {
+        if (vertices.size < 3) {
+            throw IllegalArgumentException("Polygons must have at least 3 vertices")
+        }
         if (perVertexRounding != null && perVertexRounding.size != vertices.size) {
             throw IllegalArgumentException("perVertexRounding list should be either null or " +
                     "the same size as the vertices list")
@@ -228,13 +238,12 @@
                 sideSize / expectedRoundCut to 0f
             } else if (expectedCut > sideSize) {
                 // We can do full rounding, but not full smoothing.
-                1f to (sideSize - expectedRoundCut) / expectedCut
+                1f to (sideSize - expectedRoundCut) / (expectedCut - expectedRoundCut)
             } else {
                 // There is enough room for rounding & smoothing.
-                0f to 1f
+                1f to 1f
             }
         }
-
         // Create and store list of beziers for each [potentially] rounded corner
         for (i in 0 until n) {
             // allowedCuts[0] is for the side from the previous corner to this one,
@@ -242,7 +251,7 @@
             val allowedCuts = (0..1).map { delta ->
                 val (roundCutRatio, cutRatio) = cutAdjusts[(i + n - 1 + delta) % n]
                 roundedCorners[i].expectedRoundCut * roundCutRatio +
-                    roundedCorners[i].expectedCut * cutRatio
+                    (roundedCorners[i].expectedCut - roundedCorners[i].expectedRoundCut) * cutRatio
             }
             corners.add(
                 roundedCorners[i].getCubics(
@@ -274,7 +283,22 @@
         cubicShape.updateCubics(cubics)
     }
 
-    // Transforms as usual, plus the polygon's center
+    /**
+     * Transforms (scales, rotates, and translates) the polygon by the given matrix.
+     * Note that this operation alters the points in the polygon directly; the original
+     * points are not retained, nor is the matrix itself. Thus calling this function
+     * twice with the same matrix will composite the effect. For example, a matrix which
+     * scales by 2 will scale the polygon by 2. Calling transform twice with that matrix
+     * will have the effect os scaling the shape size by 4.
+     *
+     * Note that [RoundedPolygon] objects created with default radius and center values will
+     * probably need to be scaled and repositioned using [transform] to be displayed correctly
+     * in the UI. Polygons are created by default on the unit circle around a center
+     * of (0, 0), so the resulting geometry has a bounding box width and height of 2x2; It should
+     * be resized to fit where it will be displayed appropriately.
+     *
+     * @param matrix The matrix used to transform the polygon
+     */
     fun transform(matrix: Matrix) {
         cubicShape.transform(matrix)
         val point = scratchTransformPoint
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt
index 12bfeeb..7c4d3ec 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt
@@ -17,20 +17,79 @@
 package androidx.graphics.shapes
 
 import android.graphics.PointF
-
-private const val Sqrt2 = 1.41421356f
+import androidx.annotation.IntRange
+import kotlin.math.cos
 
 /**
- * Circle creates a square polygon shape whose four corners are rounded with circular
- * arcs.
+ * Creates a circular shape, approximating the rounding of the shape around the underlying
+ * polygon vertices.
  *
- * @param radius optional radius for the circle, default value is 1.0.
+ * @param numVertices The number of vertices in the underlying polygon with which to
+ * approximate the circle, default value is 8
+ * @param radius optional radius for the circle, default value is 1.0
  * @param center optional center for the circle, default value is (0, 0)
+ *
+ * @throws IllegalArgumentException [numVertices] must be at least 3
  */
 @JvmOverloads
-fun RoundedPolygon.Companion.circle(radius: Float = 1f, center: PointF = Zero): RoundedPolygon {
-    return RoundedPolygon(4, rounding = CornerRounding(radius), radius = radius * Sqrt2,
-        center = center)
+fun RoundedPolygon.Companion.circle(
+    @IntRange(from = 3) numVertices: Int = 8,
+    radius: Float = 1f,
+    center: PointF = Zero
+): RoundedPolygon {
+
+    if (numVertices < 3) throw IllegalArgumentException("Circle must have at least three vertices")
+
+    // Half of the angle between two adjacent vertices on the polygon
+    val theta = FloatPi / numVertices
+    // Radius of the underlying RoundedPolygon object given the desired radius of the circle
+    val polygonRadius = radius / cos(theta)
+    return RoundedPolygon(
+        numVertices, rounding = CornerRounding(radius), radius = polygonRadius,
+        center = center
+    )
+}
+
+/**
+ * Creates a rectangular shape with the given width/height around the given center.
+ * Optional rounding parameters can be used to create a rounded rectangle instead.
+ *
+ * As with all [RoundedPolygon] objects, if this shape is created with default dimensions and
+ * center, it is sized to fit within the 2x2 bounding box around a center of (0, 0) and will
+ * need to be scaled and moved using [RoundedPolygon.transform] to fit the intended area
+ * in a UI.
+ *
+ * @param width The width of the rectangle, default value is 2
+ * @param height The height of the rectangle, default value is 2
+ * @param rounding The [CornerRounding] properties of every vertex. If some vertices should
+ * have different rounding properties, then use [perVertexRounding] instead. The default
+ * rounding value is [CornerRounding.Unrounded], meaning that the polygon will use the vertices
+ * themselves in the final shape and not curves rounded around the vertices.
+ * @param perVertexRounding The [CornerRounding] properties of every vertex. If this
+ * parameter is not null, then it must be of size 4 for the four corners of the shape. If this
+ * parameter is null, then the polygon will use the [rounding] parameter for every vertex instead.
+ * The default value is null.
+ * @param center The center of the rectangle, around which all vertices will be placed
+ * equidistantly. The default center is at (0,0).
+ */
+fun RoundedPolygon.Companion.rectangle(
+    width: Float = 2f,
+    height: Float = 2f,
+    rounding: CornerRounding = CornerRounding.Unrounded,
+    perVertexRounding: List<CornerRounding>? = null,
+    center: PointF = Zero
+): RoundedPolygon {
+    val left = center.x - width / 2
+    val top = center.y - height / 2
+    val right = center.x + width / 2
+    val bottom = center.y + height / 2
+
+    return RoundedPolygon(
+        listOf(
+            PointF(right, bottom), PointF(left, bottom), PointF(left, top),
+            PointF(right, top)
+        ), rounding, perVertexRounding, center
+    )
 }
 
 /**
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
index 91a8db6..d49c55a 100644
--- a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
@@ -175,9 +175,9 @@
             // LINE 1
             // Circle
             ShapeParameters(
-                sides = 4,
+                sides = 8,
                 roundness = 1f,
-                shapeId = ShapeParameters.ShapeId.Polygon
+                shapeId = ShapeParameters.ShapeId.Circle
             ),
             //
             ShapeParameters(
@@ -230,7 +230,7 @@
                 rotation = 45f,
                 shapeId = ShapeParameters.ShapeId.Blob
             ),
-            // Scalop
+            // Scallop
             ShapeParameters(
                 sides = 12,
                 innerRadius = .928f,
@@ -250,11 +250,10 @@
             ShapeParameters(roundness = .4f, shapeId = ShapeParameters.ShapeId.CornerSE),
 
             // Non - material shapes:
-            // Square
+            // Rectangle
             ShapeParameters(
                 sides = 4,
-                rotation = 45f,
-                shapeId = ShapeParameters.ShapeId.Polygon
+                shapeId = ShapeParameters.ShapeId.Rectangle
             ),
 
             // Pentagon
@@ -272,12 +271,13 @@
                 shapeId = ShapeParameters.ShapeId.Star
             ),
 
-            // 8-Sided Star
+            // Round Rect
             ShapeParameters(
-                sides = 8,
-                innerRadius = .6f,
-                shapeId = ShapeParameters.ShapeId.Star
-            )
+                sides = 4,
+                roundness = .5f,
+                smooth = 1f,
+                shapeId = ShapeParameters.ShapeId.Rectangle
+            ),
         )
     }
     editing?.let {
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
index f4066b8..7df2d10 100644
--- a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
@@ -48,9 +48,12 @@
 import androidx.core.graphics.plus
 import androidx.graphics.shapes.CornerRounding
 import androidx.graphics.shapes.RoundedPolygon
+import androidx.graphics.shapes.circle
+import androidx.graphics.shapes.rectangle
 import androidx.graphics.shapes.star
 import kotlin.math.cos
 import kotlin.math.max
+import kotlin.math.min
 import kotlin.math.roundToInt
 import kotlin.math.sin
 
@@ -105,7 +108,7 @@
     )
 
     enum class ShapeId {
-        Star, Polygon, Triangle, Blob, CornerSE
+        Star, Polygon, Triangle, Blob, CornerSE, Circle, Rectangle
     }
 
     private fun radialToCartesian(
@@ -197,7 +200,7 @@
                         PointF(sx, sy),
                         PointF(-sx, sy),
                     ),
-                    rounding = CornerRounding(this.roundness.value, this.smooth.value),
+                    rounding = CornerRounding(min(sx, sy), this.smooth.value),
                     center = PointZero
                 )
             },
@@ -235,6 +238,40 @@
             usesSides = false,
             usesInnerRatio = false,
             usesInnerParameters = false
+        ),
+        ShapeItem(
+            "Circle", shapegen = {
+                RoundedPolygon.circle(this.sides.value.roundToInt())
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(roundness = ${this.roundness.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        rotationAsString() +
+                        "shapeId = ShapeParameters.ShapeId.Circle)"
+                )
+            },
+            usesSides = true,
+            usesInnerRatio = false,
+            usesInnerParameters = false
+        ),
+        ShapeItem(
+            "Rectangle", shapegen = {
+                RoundedPolygon.rectangle(width = 4f, height = 2f,
+                    rounding = CornerRounding(this.roundness.value, this.smooth.value),
+                )
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(roundness = ${this.roundness.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        rotationAsString() +
+                        "shapeId = ShapeParameters.ShapeId.Rectangle)"
+                )
+            },
+            usesSides = false,
+            usesInnerRatio = false,
+            usesInnerParameters = false
         )
 
         /*
diff --git a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt
index f70aa81..bff8b6b 100644
--- a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt
+++ b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt
@@ -28,6 +28,7 @@
 import androidx.graphics.shapes.CornerRounding.Companion.Unrounded
 import androidx.graphics.shapes.RoundedPolygon
 import androidx.graphics.shapes.circle
+import androidx.graphics.shapes.rectangle
 import androidx.graphics.shapes.star
 
 class ShapeActivity : Activity() {
@@ -70,8 +71,8 @@
         shapes.add(RoundedPolygon.circle())
         //        "Squirrel" to DefaultShapes.fgSquircle(0.9f),
         shapes.add(RoundedPolygon(4))
-        //        "Squircle" to DefaultShapes.fgSquircle(0.7f),
-        shapes.add(RoundedPolygon(4))
+        //        Square, using rectangle function
+        shapes.add(RoundedPolygon.rectangle(width = 2f, height = 2f))
         //        "Scallop" to DefaultShapes.Scallop,
         shapes.add(MaterialShapes.scallop())
         //        "Clover" to DefaultShapes.Clover,
@@ -79,8 +80,8 @@
 
         //        "Alice" to DefaultShapes.Alice,
         shapes.add(MaterialShapes.alice())
-        //        "Veronica" to DefaultShapes.Alice.rotate(TwoPI / 2),
-        shapes.add(RoundedPolygon(4))
+        //        Rectangle
+        shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f))
         //        "Wiggle-Star" to DefaultShapes.WiggleStar,
         shapes.add(MaterialShapes.wiggleStar())
         //        "Wovel" to DefaultShapes.Wovel,
@@ -92,12 +93,16 @@
         shapes.add(blobR2)
         //        "More" to DefaultShapes.More,
         shapes.add(MaterialShapes.more())
-        //        "Less" to DefaultShapes.More.rotate(TwoPI / 2),
-        shapes.add(RoundedPolygon(4))
-        //        "CornerNE" to DefaultShapes.CornerSE.rotate(-TwoPI / 4),
-        shapes.add(RoundedPolygon(4))
-        //        "CornerNW" to DefaultShapes.CornerSE.rotate(TwoPI / 2),
-        shapes.add(RoundedPolygon(4))
+        //        Round Rect
+        shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f,
+            rounding = CornerRounding(1f)
+        ))
+        //        Round Rect (smoothed)
+        shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f,
+            rounding = CornerRounding(1f, .5f)))
+        //        Round Rect (smoothed more)
+        shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f,
+            rounding = CornerRounding(1f, 1f)))
 
         //        "CornerSW" to DefaultShapes.CornerSE.rotate(TwoPI / 4),
         shapes.add(RoundedPolygon(4))
diff --git a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
index e3cf5c9..176f118 100644
--- a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
+++ b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
@@ -41,6 +41,7 @@
         val scaleFactor = min(scaleX, scaleY)
         return scaleFactor
     }
+
     private fun calculateMatrix(bounds: RectF): Matrix {
         val scale = calculateScale(bounds)
         val scaledLeft = scale * bounds.left
diff --git a/leanback/leanback-paging/build.gradle b/leanback/leanback-paging/build.gradle
index e0786f9..6c51eec 100644
--- a/leanback/leanback-paging/build.gradle
+++ b/leanback/leanback-paging/build.gradle
@@ -36,7 +36,7 @@
     }
     androidTestImplementation(libs.kotlinTest)
     androidTestImplementation(libs.kotlinCoroutinesTest)
-    androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
 }
 
diff --git a/lifecycle/lifecycle-extensions/build.gradle b/lifecycle/lifecycle-extensions/build.gradle
index a41fdaa..b729d31 100644
--- a/lifecycle/lifecycle-extensions/build.gradle
+++ b/lifecycle/lifecycle-extensions/build.gradle
@@ -25,8 +25,8 @@
 
 dependencies {
     api("androidx.lifecycle:lifecycle-runtime:2.2.0")
-    api("androidx.arch.core:core-common:2.1.0")
-    api("androidx.arch.core:core-runtime:2.1.0")
+    api("androidx.arch.core:core-common:2.2.0")
+    api("androidx.arch.core:core-runtime:2.2.0")
     api("androidx.fragment:fragment:1.2.0")
     api("androidx.lifecycle:lifecycle-common:2.2.0")
     api("androidx.lifecycle:lifecycle-livedata:2.2.0")
@@ -34,7 +34,7 @@
     api("androidx.lifecycle:lifecycle-service:2.2.0")
     api("androidx.lifecycle:lifecycle-viewmodel:2.2.0")
 
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
 
diff --git a/lifecycle/lifecycle-livedata-core-ktx/build.gradle b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
index c36dca9..6ab8c4e6 100644
--- a/lifecycle/lifecycle-livedata-core-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
@@ -28,7 +28,7 @@
     api(project(":lifecycle:lifecycle-livedata-core"))
     api(libs.kotlinStdlib)
     testImplementation(project(":lifecycle:lifecycle-runtime"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.junit)
diff --git a/lifecycle/lifecycle-livedata-core-truth/build.gradle b/lifecycle/lifecycle-livedata-core-truth/build.gradle
index 6f8a4c4..910f657 100644
--- a/lifecycle/lifecycle-livedata-core-truth/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-truth/build.gradle
@@ -29,7 +29,7 @@
     api(libs.kotlinStdlib)
     testImplementation(libs.truth)
     testImplementation(libs.mockitoCore4)
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(project(":internal-testutils-truth"))
 }
 
diff --git a/lifecycle/lifecycle-livedata-core/build.gradle b/lifecycle/lifecycle-livedata-core/build.gradle
index 929ec53..1947720d 100644
--- a/lifecycle/lifecycle-livedata-core/build.gradle
+++ b/lifecycle/lifecycle-livedata-core/build.gradle
@@ -24,12 +24,12 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    implementation("androidx.arch.core:core-common:2.1.0")
-    implementation("androidx.arch.core:core-runtime:2.1.0")
+    implementation("androidx.arch.core:core-common:2.2.0")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     api(project(":lifecycle:lifecycle-common"))
 
     testImplementation(project(":lifecycle:lifecycle-runtime"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.junit)
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index 97e3ebb..48f4b7b 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -30,7 +30,7 @@
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinCoroutinesTest)
diff --git a/lifecycle/lifecycle-livedata/build.gradle b/lifecycle/lifecycle-livedata/build.gradle
index cfa330b..824c7f0 100644
--- a/lifecycle/lifecycle-livedata/build.gradle
+++ b/lifecycle/lifecycle-livedata/build.gradle
@@ -24,12 +24,12 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    implementation("androidx.arch.core:core-common:2.1.0")
-    api("androidx.arch.core:core-runtime:2.1.0")
+    implementation("androidx.arch.core:core-common:2.2.0")
+    api("androidx.arch.core:core-runtime:2.2.0")
     api(project(":lifecycle:lifecycle-livedata-core"))
 
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
index 587e146..79856f6 100644
--- a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
@@ -33,7 +33,7 @@
   testImplementation(libs.truth)
   testImplementation(libs.kotlinCoroutinesTest)
   testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-  testImplementation("androidx.arch.core:core-testing:2.1.0")
+  testImplementation("androidx.arch.core:core-testing:2.2.0")
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-reactivestreams/build.gradle b/lifecycle/lifecycle-reactivestreams/build.gradle
index 6730a57..e47bb63 100644
--- a/lifecycle/lifecycle-reactivestreams/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams/build.gradle
@@ -25,7 +25,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.arch.core:core-common:2.1.0")
+    api("androidx.arch.core:core-common:2.2.0")
     api(project(":lifecycle:lifecycle-common"))
     api(project(":lifecycle:lifecycle-livedata"))
     api(project(":lifecycle:lifecycle-runtime"))
@@ -39,7 +39,7 @@
     testImplementation(libs.truth)
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-runtime-compose/api/current.txt b/lifecycle/lifecycle-runtime-compose/api/current.txt
index 0686666..5b19f2b 100644
--- a/lifecycle/lifecycle-runtime-compose/api/current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/current.txt
@@ -8,6 +8,10 @@
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
   }
 
+  public final class LifecycleEffectKt {
+    method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+  }
+
   public final class LifecycleExtKt {
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
   }
diff --git a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
index 0686666..5b19f2b 100644
--- a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
@@ -8,6 +8,10 @@
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
   }
 
+  public final class LifecycleEffectKt {
+    method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+  }
+
   public final class LifecycleExtKt {
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
   }
diff --git a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
index 0686666..5b19f2b 100644
--- a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
@@ -8,6 +8,10 @@
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
   }
 
+  public final class LifecycleEffectKt {
+    method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+  }
+
   public final class LifecycleExtKt {
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
   }
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt
index 54b6553c..e9387cd 100644
--- a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt
@@ -19,6 +19,8 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -27,7 +29,10 @@
 import kotlinx.coroutines.runBlocking
 import org.junit.Rule
 import org.junit.Test
+import org.junit.runner.RunWith
 
+@MediumTest
+@RunWith(AndroidJUnit4::class)
 class CollectAsStateWithLifecycleTests {
 
     @get:Rule
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
new file mode 100644
index 0000000..3d7e9fe
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 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.lifecycle.compose
+
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.setMain
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class LifecycleEffectTest {
+
+    private val dispatcher = UnconfinedTestDispatcher()
+    private lateinit var lifecycleOwner: TestLifecycleOwner
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Before
+    fun setup() {
+        lifecycleOwner = TestLifecycleOwner(coroutineDispatcher = dispatcher)
+        Dispatchers.setMain(dispatcher)
+    }
+
+    @Test
+    fun lifecycleEventEffectTest_noEvent() {
+        var stopCount = 0
+
+        composeTestRule.waitForIdle()
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
+                    stopCount++
+                }
+            }
+        }
+
+        composeTestRule.runOnIdle {
+            assertWithMessage("Lifecycle should not have been stopped")
+                .that(stopCount)
+                .isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun lifecycleEventEffectTest_localLifecycleOwner() {
+        val expectedEvent = Lifecycle.Event.ON_STOP
+        var stopCount = 0
+
+        composeTestRule.waitForIdle()
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                LifecycleEventEffect(expectedEvent) {
+                    stopCount++
+                }
+            }
+        }
+
+        composeTestRule.runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(expectedEvent)
+            assertWithMessage("Lifecycle should have been stopped")
+                .that(stopCount)
+                .isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun lifecycleEventEffectTest_customLifecycleOwner() {
+        val expectedEvent = Lifecycle.Event.ON_STOP
+        var stopCount = 0
+
+        composeTestRule.waitForIdle()
+        composeTestRule.setContent {
+            LifecycleEventEffect(expectedEvent, lifecycleOwner) {
+                stopCount++
+            }
+        }
+
+        composeTestRule.runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(expectedEvent)
+            assertWithMessage("Lifecycle should have been stopped")
+                .that(stopCount)
+                .isEqualTo(1)
+        }
+    }
+}
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
index 255d06a..a9e7d4e 100644
--- a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
@@ -19,14 +19,16 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
 class LifecycleExtTest {
 
     @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@@ -52,9 +54,12 @@
             assertThat(realStateValue).isEqualTo(Lifecycle.State.INITIALIZED)
         }
 
+        // TODO(b/280362188): commenting this portion out until bug is fixed
+        /*
         lifecycleOwner.currentState = Lifecycle.State.RESUMED
         rule.runOnIdle {
             assertThat(realStateValue).isEqualTo(Lifecycle.State.RESUMED)
         }
+        */
     }
 }
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
new file mode 100644
index 0000000..c9e47ae
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2023 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.lifecycle.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+
+/**
+ * Schedule an effect to run when the [Lifecycle] receives a specific [Lifecycle.Event].
+ *
+ * Using a [LifecycleEventObserver] to listen for when [LifecycleEventEffect] enters
+ * the composition, [onEvent] will be launched when receiving the specified [event].
+ *
+ * This function should **not** be used to listen for [Lifecycle.Event.ON_DESTROY] because
+ * Compose stops recomposing after receiving a [Lifecycle.Event.ON_STOP] and will never be
+ * aware of an ON_DESTROY to launch [onEvent].
+ *
+ * This function should also **not** be used to launch tasks in response to callback
+ * events by way of storing callback data as a [Lifecycle.State] in a [MutableState].
+ * Instead, see [currentStateAsState] to obtain a [State<Lifecycle.State>][State]
+ * that may be used to launch jobs in response to state changes.
+ *
+ * @param event The [Lifecycle.Event] to listen for
+ * @param lifecycleOwner The lifecycle owner to attach an observer
+ * @param onEvent The effect to be launched when we receive an [event] callback
+ *
+ * @throws IllegalArgumentException if attempting to listen for [Lifecycle.Event.ON_DESTROY]
+ */
+@Composable
+fun LifecycleEventEffect(
+    event: Lifecycle.Event,
+    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
+    onEvent: () -> Unit
+) {
+    if (event == Lifecycle.Event.ON_DESTROY) {
+        throw IllegalArgumentException("LifecycleEventEffect cannot be used to " +
+            "listen for Lifecycle.Event.ON_DESTROY, since Compose disposes of the " +
+            "composition before ON_DESTROY observers are invoked.")
+    }
+
+    // Safely update the current `onEvent` lambda when a new one is provided
+    val currentOnEvent by rememberUpdatedState(onEvent)
+    DisposableEffect(lifecycleOwner) {
+        val observer = LifecycleEventObserver { _, e ->
+            if (e == event) {
+                currentOnEvent()
+            }
+        }
+
+        lifecycleOwner.lifecycle.addObserver(observer)
+
+        onDispose {
+            lifecycleOwner.lifecycle.removeObserver(observer)
+        }
+    }
+}
\ No newline at end of file
diff --git a/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java b/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java
index 7c2d47d..0a10e27 100644
--- a/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java
+++ b/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java
@@ -22,14 +22,20 @@
  * Android IDL Language.
  */
 public class AidlLanguage extends Language {
+  private static final Object INSTANCE_LOCK = new Object();
+
   public static final Language INSTANCE = getOrCreate();
 
   private static Language getOrCreate() {
-    Language lang = Language.findLanguageByID(ID);
-    if (lang != null) {
-      return lang;
+    // The Language class is not thread-safe, so this is a best-effort to avoid a race condition
+    // during our own access across multiple lint worker threads.
+    synchronized (INSTANCE_LOCK) {
+      Language lang = Language.findLanguageByID(ID);
+      if (lang != null) {
+        return lang;
+      }
+      return new AidlLanguage();
     }
-    return new AidlLanguage();
   }
 
   @NonNls private static final String ID = "AIDL";
diff --git a/navigation/CHANGELOG.md b/navigation/CHANGELOG.md
new file mode 100644
index 0000000..0f9c1e1
--- /dev/null
+++ b/navigation/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Log for changes in the Navigation library
+#
+# `Added`: for new features
+# `Changed`: for changes in existing functionality
+# `Deprecated`: for soon to be removed functionality
+# `Removed`: for now removed feature
+# `Fixed`: for any bug fixes
+# `Security`: in case of vulnerabilities
+
+# Unreleased
+
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index 72b827e..954e0da 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -45,7 +45,7 @@
 
     api(libs.kotlinStdlib)
     testImplementation(project(":navigation:navigation-testing"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index e56823a..e722e52 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -35,7 +35,7 @@
     api(libs.playFeatureDelivery)
 
     testImplementation(project(":navigation:navigation-testing"))
-    testImplementation("androidx.arch.core:core-testing:2.1.0")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.testCore)
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testRunner)
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
index 4dc1246..a8e6f2d 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
@@ -31,7 +31,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
-/* ktlint-disable no-unused-imports */ // https://github.com/pinterest/ktlint/issues/937
 import org.mockito.Mockito.`when` as mockWhen
 
 /* ktlint-enable unused-imports */
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
index deda0ab..9e0ac66 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
@@ -22,11 +22,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-/* ktlint-disable no-unused-imports */ // https://github.com/pinterest/ktlint/issues/937
-import org.mockito.Mockito.`when` as mockWhen
-/* ktlint-enable unused-imports */
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when` as mockWhen
 
 @RunWith(JUnit4::class)
 public class DynamicInstallManagerTest {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index dcc0f5c..8b35f7b 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -610,6 +610,23 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateNullGraph() {
+        val navController = createNavController()
+        val deepLinkRequest = NavDeepLinkRequest.Builder.fromUri(
+            Uri.parse("android-app://androidx.navigation.test/destination")
+        ).build()
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLinkRequest)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Cannot navigate to $deepLinkRequest. Navigation graph has not " +
+                "been set for NavController $navController."
+        )
+    }
+
+    @UiThreadTest
+    @Test
     fun testInvalidNavigateViaDeepLink() {
         val navController = createNavController()
         navController.setGraph(R.navigation.nav_simple)
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index 8486793..be6eafb 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -1772,6 +1772,10 @@
         navOptions: NavOptions?,
         navigatorExtras: Navigator.Extras?
     ) {
+        requireNotNull(_graph) {
+            "Cannot navigate to $request. Navigation graph has not been set for " +
+                "NavController $this."
+        }
         val deepLinkMatch = _graph!!.matchDeepLink(request)
         if (deepLinkMatch != null) {
             val destination = deepLinkMatch.destination
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index 5ae97ce..db00ce6 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -24,7 +24,7 @@
 }
 
 dependencies {
-    implementation("androidx.arch.core:core-runtime:2.1.0")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation(projectOrArtifact(":room:room-ktx"))
     implementation(projectOrArtifact(":room:room-rxjava2"))
     implementation(projectOrArtifact(":room:room-paging"))
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index e2ce56b..00ad658 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -33,7 +33,7 @@
     }
 
     api("androidx.annotation:annotation:1.3.0")
-    api("androidx.arch.core:core-common:2.1.0")
+    api("androidx.arch.core:core-common:2.2.0")
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
 
diff --git a/paging/paging-runtime/build.gradle b/paging/paging-runtime/build.gradle
index 013bbd8..47fdac8 100644
--- a/paging/paging-runtime/build.gradle
+++ b/paging/paging-runtime/build.gradle
@@ -50,7 +50,7 @@
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
-    androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.kotlinTest)
     androidTestImplementation(libs.kotlinCoroutinesTest)
diff --git a/paging/paging-rxjava2-ktx/build.gradle b/paging/paging-rxjava2-ktx/build.gradle
index b298111..a355219 100644
--- a/paging/paging-rxjava2-ktx/build.gradle
+++ b/paging/paging-rxjava2-ktx/build.gradle
@@ -31,7 +31,7 @@
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(libs.espressoCore)
 }
 
diff --git a/paging/paging-rxjava2/build.gradle b/paging/paging-rxjava2/build.gradle
index da06741d..c830a91 100644
--- a/paging/paging-rxjava2/build.gradle
+++ b/paging/paging-rxjava2/build.gradle
@@ -25,7 +25,7 @@
 
 dependencies {
     api(project(":paging:paging-common"))
-    api("androidx.arch.core:core-runtime:2.1.0")
+    api("androidx.arch.core:core-runtime:2.2.0")
     api(libs.rxjava2)
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesRx2)
@@ -41,7 +41,7 @@
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
-    androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
 
     samples(project(":paging:paging-samples"))
 }
diff --git a/paging/paging-rxjava3/build.gradle b/paging/paging-rxjava3/build.gradle
index f69969b..dc27213 100644
--- a/paging/paging-rxjava3/build.gradle
+++ b/paging/paging-rxjava3/build.gradle
@@ -25,7 +25,7 @@
 
 dependencies {
     api(project(":paging:paging-common"))
-    api("androidx.arch.core:core-runtime:2.1.0")
+    api("androidx.arch.core:core-runtime:2.2.0")
     api(libs.rxjava3)
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesRx3)
@@ -41,7 +41,7 @@
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
-    androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
 }
 
 androidx {
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
index 5927a01..de51a6e 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
@@ -20,8 +20,10 @@
 
 import static org.junit.Assert.assertThrows;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.net.Uri;
+import android.util.Log;
 
 import androidx.annotation.RequiresApi;
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig;
@@ -71,7 +73,7 @@
     private static final String TAG = "FledgeCtsDebuggableTest";
 
     // Configuration constants
-    private static final int SDK_MAX_REQUEST_PERMITS_PER_SECOND = 1000;
+    private static final int SDK_MAX_REQUEST_PERMITS_PER_SECOND = 1;
     // sleep time to prevent hitting rate limit, with a small tolerance to prevent edge
     // case of max calls per second falling exactly within one second
     private static final long DEFAULT_API_RATE_LIMIT_SLEEP_MS =
@@ -204,7 +206,7 @@
                 new CustomAudienceClient(sContext);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
     }
 
     @Test
@@ -225,7 +227,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -288,7 +290,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -343,7 +345,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -400,7 +402,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -447,7 +449,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -505,7 +507,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -551,7 +553,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -616,7 +618,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -673,14 +675,14 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // Wait to ensure that CA2 gets expired
-        Thread.sleep(caTimeToExpireSeconds * 2 * 1000);
+        doSleep(caTimeToExpireSeconds * 2 * 1000);
 
         // Running ad selection and asserting that the outcome is returned in < 10 seconds
         AdSelectionOutcome outcome =
@@ -727,7 +729,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -774,7 +776,7 @@
                 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
         // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
-        Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+        doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
 
         mCustomAudienceClient
                 .joinCustomAudience(customAudience2)
@@ -808,6 +810,23 @@
         assertThat(selectAdsException.getCause()).isInstanceOf(TimeoutException.class);
     }
 
+    @SuppressLint("BanThreadSleep")
+    private static void doSleep(long timeout) {
+        Log.i(TAG, String.format("Starting to sleep for %d ms", timeout));
+        long currentTime = System.currentTimeMillis();
+        long wakeupTime = currentTime + timeout;
+        while (wakeupTime - currentTime > 0) {
+            Log.i(TAG, String.format("Time left to sleep: %d ms", wakeupTime - currentTime));
+            try {
+                Thread.sleep(wakeupTime - currentTime);
+            } catch (InterruptedException ignored) {
+
+            }
+            currentTime = System.currentTimeMillis();
+        }
+        Log.i(TAG, "Done sleeping");
+    }
+
     private static Uri getUri(String authority, String path) {
         return Uri.parse("https://" + authority + path);
     }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
index 3c40caa0..4c4c071 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
@@ -225,7 +225,7 @@
                     class NonAnnotatedClass
                 """
             ), Source.java(
-                "com/mysdk/NonAnnotatedJavaClass.java", """
+                "com/mysdk/NonAnnotatedJavaClass", """
                     package com.mysdk;
                     class NonAnnotatedJavaClass {}
                 """
@@ -265,7 +265,7 @@
     @Test
     fun nonKotlinAnnotatedInterface_throws() {
         val source = Source.java(
-            "com/mysdk/MySdk.java", """
+            "com/mysdk/MySdk", """
                     package com.mysdk;
                     import androidx.privacysandbox.tools.PrivacySandboxService;
                     @PrivacySandboxService
diff --git a/room/benchmark/build.gradle b/room/benchmark/build.gradle
index 7758551..d10928f 100644
--- a/room/benchmark/build.gradle
+++ b/room/benchmark/build.gradle
@@ -30,7 +30,7 @@
     // depend on the shadowed version so that it tests with the shipped artifact
     kaptAndroidTest project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
     androidTestImplementation(project(":room:room-rxjava2"))
-    androidTestImplementation("androidx.arch.core:core-runtime:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-runtime:2.2.0")
     androidTestImplementation(projectOrArtifact(":benchmark:benchmark-junit4"))
     androidTestImplementation(libs.rxjava2)
     androidTestImplementation(libs.junit)
diff --git a/room/integration-tests/autovaluetestapp/build.gradle b/room/integration-tests/autovaluetestapp/build.gradle
index c2b23f1..e44eda7 100644
--- a/room/integration-tests/autovaluetestapp/build.gradle
+++ b/room/integration-tests/autovaluetestapp/build.gradle
@@ -29,7 +29,7 @@
 dependencies {
     implementation(project(":room:room-common"))
     implementation(project(":room:room-runtime"))
-    implementation(projectOrArtifact(":arch:core:core-runtime"))
+    implementation("androidx.arch.core:core-runtime:2.2.0")
 
     // depend on the shadowed version so that it tests with the shipped artifact
     androidTestAnnotationProcessor project(path: ":room:room-compiler",
@@ -37,9 +37,8 @@
     androidTestAnnotationProcessor(libs.autoValue)
     androidTestAnnotationProcessor(libs.autoValueParcel)
 
-    androidTestImplementation(projectOrArtifact(":arch:core:core-runtime")) // Added for b/155802460
     androidTestImplementation(project(":room:room-testing"))
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(libs.autoValueAnnotations)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 6556553..8241224 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -75,7 +75,7 @@
     implementation(project(":room:room-common"))
     implementation(project(":room:room-runtime"))
     implementation(project(":room:room-paging"))
-    implementation(projectOrArtifact(":arch:core:core-runtime"))
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesAndroid)
@@ -91,7 +91,7 @@
     kaptAndroidTestWithKapt(
             project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
     )
-    androidTestImplementation(projectOrArtifact(":arch:core:core-runtime"))
+    androidTestImplementation("androidx.arch.core:core-runtime:2.2.0")
     androidTestImplementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
@@ -118,7 +118,7 @@
     androidTestImplementation(project(":room:room-rxjava3"))
     androidTestImplementation(project(":room:room-ktx"))
     androidTestImplementation(project(":internal-testutils-common"))
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation("androidx.paging:paging-runtime:3.1.1")
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
     androidTestImplementation(libs.rxjava2)
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index fea1c86..1975a13 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -93,7 +93,7 @@
 dependencies {
     implementation(project(":room:room-common"))
     implementation(project(":room:room-runtime"))
-    implementation(projectOrArtifact(":arch:core:core-runtime"))
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation("androidx.lifecycle:lifecycle-livedata:2.6.1")
     implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
     implementation(libs.multidex)
@@ -113,14 +113,14 @@
             configuration: "shadowAndImplementation")
 
     androidTestImplementation(project(":annotation:annotation-experimental"))
-    androidTestImplementation(projectOrArtifact(":arch:core:core-runtime"))
-    androidTestImplementation(projectOrArtifact(":arch:core:core-common"))
+    androidTestImplementation("androidx.arch.core:core-runtime:2.2.0")
+    androidTestImplementation("androidx.arch.core:core-common:2.2.0")
     androidTestImplementation(project(":room:room-testing"))
     androidTestImplementation(project(":room:room-rxjava2"))
     androidTestImplementation(project(":room:room-rxjava3"))
     androidTestImplementation(project(":room:room-guava"))
     androidTestImplementation(project(":room:room-paging"))
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(projectOrArtifact(":paging:paging-runtime"))
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt
index c62cfab..4860763 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt
@@ -79,6 +79,9 @@
             @Language("java")
             code: String
         ): Source {
+            require(!qName.endsWith(".java")) {
+                "Please exclude extension `.java` for Java sources."
+            }
             return JavaSource(
                 qName,
                 code
@@ -90,6 +93,9 @@
             @Language("kotlin")
             code: String
         ): Source {
+            require(filePath.endsWith(".kt")) {
+                "Please include extension `.kt` for Kotlin sources."
+            }
             return KotlinSource(
                 filePath,
                 code
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt
index 81e443e..8287cfd 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt
@@ -101,7 +101,7 @@
     if (hasKotlinSource) return this
     return copy(
         sources = sources + Source.kotlin(
-            "SyntheticSource",
+            "SyntheticSource.kt",
             code = """
                 package xprocessing.generated
                 class SyntheticKotlinSource
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
index 7ded92a..cf0ef44 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
@@ -185,7 +185,7 @@
     @Test
     fun cleanKotlinCompilationHasNoWarnings() {
         val kotlinSource = Source.kotlin(
-            "Subject",
+            "Subject.kt",
             """
             package foo.bar
             class Subject {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
index 21395f6..a2b2895 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
@@ -66,8 +66,8 @@
 
     @Test
     fun outOfOrderJava_fields() {
-        val libSource = Source.kotlin(
-            "JavaClass.java",
+        val libSource = Source.java(
+            "JavaClass",
             """
             class JavaClass {
                 String b;
@@ -116,8 +116,8 @@
 
     @Test
     fun outOfOrderJava_methods() {
-        val libSource = Source.kotlin(
-            "JavaClass.java",
+        val libSource = Source.java(
+            "JavaClass",
             """
             class JavaClass {
                 String b() { return ""; }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index ad37a87..7a761a0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -436,7 +436,7 @@
             """.trimIndent()
         )
         val javaSrc = Source.java(
-            "JavaClass.java",
+            "JavaClass",
             """
             import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults;
             @JavaAnnotationWithDefaults
@@ -499,7 +499,7 @@
     @Test
     fun javaPrimitiveArray() {
         val javaSrc = Source.java(
-            "JavaSubject.java",
+            "JavaSubject",
             """
             import androidx.room.compiler.processing.testcode.*;
             class JavaSubject {
@@ -592,7 +592,7 @@
     @Test
     fun javaEnum() {
         val javaSrc = Source.java(
-            "JavaSubject.java",
+            "JavaSubject",
             """
             import androidx.room.compiler.processing.testcode.*;
             class JavaSubject {
@@ -632,7 +632,7 @@
     @Test
     fun javaEnumArray() {
         val javaSrc = Source.java(
-            "JavaSubject.java",
+            "JavaSubject",
             """
             import androidx.room.compiler.processing.testcode.*;
             class JavaSubject {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 973e82a..976ab4f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -598,7 +598,7 @@
             """.trimIndent()
         )
         val javaSrc = Source.java(
-            "JavaClass.java",
+            "JavaClass",
             """
             import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults;
             @JavaAnnotationWithDefaults
@@ -681,7 +681,7 @@
     fun javaPrimitiveArray() {
         // TODO: expand this test for other primitive types: 179081610
         val javaSrc = Source.java(
-            "JavaSubject.java",
+            "JavaSubject",
             """
             import androidx.room.compiler.processing.testcode.*;
             class JavaSubject {
@@ -720,7 +720,7 @@
     @Test
     fun javaEnum() {
         val javaSrc = Source.java(
-            "JavaSubject.java",
+            "JavaSubject",
             """
             import androidx.room.compiler.processing.testcode.*;
             class JavaSubject {
@@ -759,7 +759,7 @@
     @Test
     fun javaEnumArray() {
         val javaSrc = Source.java(
-            "JavaSubject.java",
+            "JavaSubject",
             """
             import androidx.room.compiler.processing.testcode.*;
             class JavaSubject {
@@ -1151,7 +1151,7 @@
             """.trimIndent()
         )
         val javaSource = Source.java(
-            "foo.bar.Subject.java",
+            "foo.bar.Subject",
             """
             package foo.bar;
             import java.lang.annotation.ElementType;
@@ -1232,7 +1232,7 @@
             """.trimIndent()
         )
         val javaSource = Source.java(
-            "foo.bar.Subject.java",
+            "foo.bar.Subject",
             """
             package foo.bar;
             import java.lang.annotation.ElementType;
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index bc037bc..4c88042 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -44,7 +44,7 @@
                     """.trimIndent()
                 ),
                 Source.java(
-                    "JavaClass.java",
+                    "JavaClass",
                     """
                     abstract class JavaClass<T> {
                         JavaClass(T t) {}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
index 4091a00..c540af4 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
@@ -31,7 +31,7 @@
         runProcessorTest(
             sources = listOf(
                 Source.java(
-                    "Foo.java",
+                    "Foo",
                     """
                     class Foo {}
                     """.trimIndent()
@@ -56,7 +56,7 @@
         runProcessorTest(
             sources = listOf(
                 Source.java(
-                    "Foo.java",
+                    "Foo",
                     """
                     class Foo {}
                     """.trimIndent()
@@ -80,7 +80,7 @@
         runProcessorTest(
             sources = listOf(
                 Source.java(
-                    "Foo.java",
+                    "Foo",
                     """
                     class Foo {}
                     """.trimIndent()
@@ -104,7 +104,7 @@
         runProcessorTest(
             sources = listOf(
                 Source.java(
-                    "Foo.java",
+                    "Foo",
                     """
                     class Foo {}
                     """.trimIndent()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 4786cff..313e735c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -385,7 +385,7 @@
     @Test
     fun makeNullable_void() {
         val src = Source.java(
-            "Foo.java",
+            "Foo",
             """
             class Foo {
                 void subject() {}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 0d60682..33ff8fc 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -223,7 +223,7 @@
     @Test
     fun errorLogFailsCompilation() {
         val src = Source.java(
-            "Foo.java",
+            "Foo",
             """
             class Foo {}
             """.trimIndent()
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 134a9c2..0e44884 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
@@ -426,7 +426,7 @@
             """.trimIndent()
         )
         val javaSrc = Source.java(
-            "Bar.java",
+            "Bar",
             """
             class JavaClass {}
             interface JavaInterface {}
@@ -728,7 +728,7 @@
         runTest(
             listOf(
                 Source.java(
-                    "JavaSubject.java",
+                    "JavaSubject",
                     """
                     class JavaSubject {
                         int myField;
@@ -752,7 +752,7 @@
                 )
             ),
         ) { invocation ->
-            listOf("JavaSubject", "KotlinSubject",).map {
+            listOf("JavaSubject", "KotlinSubject").map {
                 invocation.processingEnv.requireTypeElement(it)
             }.forEach { subject ->
                 val methods = subject.getDeclaredMethods()
@@ -2075,6 +2075,272 @@
         }
     }
 
+    @Test
+    fun javaFieldDescriptors() {
+        runTest(
+            sources = listOf(
+                Source.java(
+                    "TestClassA",
+                    """
+                    import java.util.List;
+                    class TestClassA<T> {
+                        int field1;
+                        String field2;
+                        T field3;
+                        List<String> field4;
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassA")
+            assertThat(foo.getDeclaredFields().map { it.jvmDescriptor }.toList())
+                .containsExactly(
+                    "field1:I",
+                    "field2:Ljava/lang/String;",
+                    "field3:Ljava/lang/Object;",
+                    "field4:Ljava/util/List;"
+                )
+        }
+    }
+
+    @Test
+    fun javaMethodDescriptorsPrimitives() {
+        runTest(
+            sources = listOf(
+                Source.java(
+                    "TestClassB",
+                    """
+                    class TestClassB<T> {
+                        void method1(boolean yesOrNo, int number) {}
+
+                        byte method2(char letter) {
+                          return 0;
+                        }
+
+                        void method3(double realNumber1, float realNummber2) {}
+
+                        void method4(long bigNumber, short littlerNumber) {}
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassB")
+            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+                .containsExactly(
+                    "method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V"
+                )
+        }
+    }
+
+    @Test
+    fun javaMethodDescriptorsJavaTypes() {
+        runTest(
+            sources = listOf(
+                Source.java(
+                    "TestClassC",
+                    """
+                    import java.util.*;
+                    class TestClassC<T> {
+                        void method1(Object something) {}
+
+                        Object method2() {
+                          return null;
+                        }
+
+                        List<String> method3(ArrayList<Integer> list) {
+                          return null;
+                        }
+
+                        Map<String, Object> method4() {
+                          return null;
+                        }
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassC")
+            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+                .containsExactly(
+                    "method1(Ljava/lang/Object;)V",
+                    "method2()Ljava/lang/Object;",
+                    "method3(Ljava/util/ArrayList;)Ljava/util/List;",
+                    "method4()Ljava/util/Map;"
+                )
+        }
+    }
+
+    @Test
+    fun javaMethodDescriptorsTestTypes() {
+        runTest(
+            sources = listOf(
+                Source.java(
+                    "TestClassD",
+                    """
+                    class TestDataClass {}
+                    class TestClassD<T> {
+                        void method1(TestDataClass data) {}
+
+                        TestDataClass method2() {
+                          return null;
+                        }
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassD")
+            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+                .containsExactly(
+                    "method1(LTestDataClass;)V",
+                    "method2()LTestDataClass;"
+                )
+        }
+    }
+
+    @Test
+    fun javaMethodDescriptorsArrays() {
+        runTest(
+            sources = listOf(
+                Source.java(
+                    "TestClassE",
+                    """
+                    class TestDataClass {}
+                    class TestClassE<T> {
+                        void method1(TestDataClass[] data) {}
+
+                        TestDataClass[] method2() {
+                          return null;
+                        }
+
+                        void method3(int[] array) {}
+
+                        void method4(int... array) {}
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassE")
+            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+                .containsExactly(
+                    "method1([LTestDataClass;)V",
+                    "method2()[LTestDataClass;",
+                    "method3([I)V",
+                    "method4([I)V"
+                )
+        }
+    }
+
+    @Test
+    fun javaMethodDescriptorsInnerTestType() {
+        runTest(
+            // KSP can't see nested types if the filename does not match the name of the
+            // enclosing class.
+            sources = listOf(
+                Source.java(
+                    "TestDataClass",
+                    """
+                    public class TestDataClass {
+                        class MemberInnerData {}
+
+                        static class StaticInnerData {}
+
+                        enum EnumData {
+                          VALUE1,
+                          VALUE2
+                        }
+                    }
+                    """.trimIndent()
+                ),
+                Source.java(
+                    "TestClassF",
+                    """
+                    class TestClassF<T> {
+                        void method1(TestDataClass.MemberInnerData data) {}
+
+                        void method2(TestDataClass.StaticInnerData data) {}
+
+                        void method3(TestDataClass.EnumData enumData) {}
+
+                        TestDataClass.StaticInnerData method4() {
+                          return null;
+                        }
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassF")
+            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+                .containsExactly(
+                    "method1(LTestDataClass\$MemberInnerData;)V",
+                    "method2(LTestDataClass\$StaticInnerData;)V",
+                    "method3(LTestDataClass\$EnumData;)V",
+                    "method4()LTestDataClass\$StaticInnerData;"
+                )
+        }
+    }
+
+    @Test
+    fun methodDescriptorsErasure() {
+        runTest(
+            sources = listOf(
+                Source.java(
+                    "TestClassG",
+                    """
+                    import java.util.*;
+                    class TestClassG<T> {
+                        void method1(T something) {}
+                        T method2() {
+                          return null;
+                        }
+                        List<? extends String> method3() {
+                          return null;
+                        }
+                        Map<T, String> method4() {
+                          return null;
+                        }
+                        ArrayList<Map<T, String>> method5() {
+                          return null;
+                        }
+                        static <I, O extends I> O method6(I input) {
+                          return null;
+                        }
+                        static <I, O extends String> O method7(I input) {
+                          return null;
+                        }
+                        static <P extends Collection<String> & Comparable<String>> P method8() {
+                          return null;
+                        }
+                        static <P extends String & List<Character>> P method9() {
+                          return null;
+                        }
+                    }
+                    """.trimIndent()
+                )
+            )
+        ) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("TestClassG")
+            if (!invocation.isKsp) {
+                assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+                    .containsExactly(
+                        "method1(Ljava/lang/Object;)V",
+                        "method2()Ljava/lang/Object;",
+                        "method3()Ljava/util/List;",
+                        "method4()Ljava/util/Map;",
+                        "method5()Ljava/util/ArrayList;",
+                        "method6(Ljava/lang/Object;)Ljava/lang/Object;",
+                        "method7(Ljava/lang/Object;)Ljava/lang/String;",
+                        "method8()Ljava/util/Collection;",
+                        "method9()Ljava/lang/String;"
+                    )
+            }
+        }
+    }
+
     /**
      * it is good to exclude methods coming from Object when testing as they differ between KSP
      * and KAPT but irrelevant for Room.
diff --git a/room/room-guava/build.gradle b/room/room-guava/build.gradle
index ef2b7b79..99c5ad7 100644
--- a/room/room-guava/build.gradle
+++ b/room/room-guava/build.gradle
@@ -26,7 +26,7 @@
     api(project(":room:room-runtime")) {
         exclude group: "com.google.guava", module: "listenablefuture"
     }
-    implementation("androidx.arch.core:core-runtime:2.0.1")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     api("androidx.annotation:annotation:1.0.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     androidTestImplementation(libs.testRunner)
diff --git a/room/room-paging-guava/build.gradle b/room/room-paging-guava/build.gradle
index 723c5da..e40d8bc 100644
--- a/room/room-paging-guava/build.gradle
+++ b/room/room-paging-guava/build.gradle
@@ -38,7 +38,7 @@
     androidTestImplementation(libs.kotlinCoroutinesTest)
     androidTestImplementation(libs.kotlinCoroutinesGuava)
     androidTestImplementation(libs.guavaAndroid)
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(project(":internal-testutils-common"))
     kspAndroidTest(
             project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
diff --git a/room/room-paging-rxjava2/build.gradle b/room/room-paging-rxjava2/build.gradle
index d4f2bd4..d9b9d5d 100644
--- a/room/room-paging-rxjava2/build.gradle
+++ b/room/room-paging-rxjava2/build.gradle
@@ -37,7 +37,7 @@
     androidTestImplementation(libs.kotlinTestJunit) //
     androidTestImplementation(libs.kotlinCoroutinesTest)
     androidTestImplementation(libs.kotlinCoroutinesRx2)
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(project(":internal-testutils-common"))
     kspAndroidTest(
             project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
diff --git a/room/room-paging-rxjava3/build.gradle b/room/room-paging-rxjava3/build.gradle
index 11a3bb65..a4ef997 100644
--- a/room/room-paging-rxjava3/build.gradle
+++ b/room/room-paging-rxjava3/build.gradle
@@ -37,7 +37,7 @@
     androidTestImplementation(libs.kotlinCoroutinesRx3)
     androidTestImplementation(libs.kotlinTestJunit) //
     androidTestImplementation(libs.kotlinCoroutinesTest)
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(project(":internal-testutils-common"))
     kspAndroidTest(
             project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
index adbfb9b..31a7c7f 100644
--- a/room/room-paging/build.gradle
+++ b/room/room-paging/build.gradle
@@ -50,7 +50,7 @@
             project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
     )
     androidTestImplementation(libs.truth)
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(project(":internal-testutils-common"))
     androidTestImplementation(projectOrArtifact(":paging:paging-testing"))
 }
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index aff3eb8..5859784 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -41,7 +41,7 @@
     api(project(":room:room-common"))
     api(project(":sqlite:sqlite-framework"))
     api(project(":sqlite:sqlite"))
-    implementation("androidx.arch.core:core-runtime:2.0.1")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     compileOnly("androidx.collection:collection:1.2.0")
     compileOnly("androidx.paging:paging-common:2.0.0")
     compileOnly("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
@@ -49,7 +49,7 @@
     compileOnly libs.kotlinStdlib // Due to :annotation-experimental
     lintChecks(project(":room:room-runtime-lint"))
 
-    testImplementation("androidx.arch.core:core-testing:2.0.1")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
@@ -68,7 +68,7 @@
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":internal-testutils-truth")) // for assertThrows
-    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
 
 }
 
diff --git a/room/room-rxjava2/build.gradle b/room/room-rxjava2/build.gradle
index a9a4adb..6cc2e1e 100644
--- a/room/room-rxjava2/build.gradle
+++ b/room/room-rxjava2/build.gradle
@@ -27,14 +27,14 @@
     api(project(":room:room-runtime"))
     api(libs.rxjava2)
 
-    implementation("androidx.arch.core:core-runtime:2.0.1")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation(libs.kotlinStdlib)
 
     testImplementation(libs.truth)
     testImplementation(libs.kotlinTest)
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
-    testImplementation("androidx.arch.core:core-testing:2.0.1")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation("androidx.lifecycle:lifecycle-livedata:2.0.0") // for mocking invalidation tracker
 }
 
diff --git a/room/room-rxjava3/build.gradle b/room/room-rxjava3/build.gradle
index 8369f03..fee4abe 100644
--- a/room/room-rxjava3/build.gradle
+++ b/room/room-rxjava3/build.gradle
@@ -28,14 +28,14 @@
     api(project(":room:room-runtime"))
     api(libs.rxjava3)
 
-    implementation("androidx.arch.core:core-runtime:2.0.1")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation(libs.kotlinStdlib)
 
     testImplementation(libs.truth)
     testImplementation(libs.kotlinTest)
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
-    testImplementation("androidx.arch.core:core-testing:2.0.1")
+    testImplementation("androidx.arch.core:core-testing:2.2.0")
     testImplementation("androidx.lifecycle:lifecycle-livedata:2.0.0") // for mocking invalidation tracker
 }
 
diff --git a/room/room-testing/build.gradle b/room/room-testing/build.gradle
index 3fc9bce..1471e49 100644
--- a/room/room-testing/build.gradle
+++ b/room/room-testing/build.gradle
@@ -37,7 +37,7 @@
     api(project(":sqlite:sqlite-framework"))
     api(project(":room:room-migration"))
     api(libs.junit)
-    implementation("androidx.arch.core:core-runtime:2.0.1")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
diff --git a/savedstate/savedstate/build.gradle b/savedstate/savedstate/build.gradle
index a102643..f9ca694 100644
--- a/savedstate/savedstate/build.gradle
+++ b/savedstate/savedstate/build.gradle
@@ -15,7 +15,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.arch.core:core-common:2.1.0")
+    implementation("androidx.arch.core:core-common:2.2.0")
     implementation("androidx.lifecycle:lifecycle-common:2.6.1")
     api(libs.kotlinStdlib)
 
diff --git a/settings.gradle b/settings.gradle
index aa1e705..9abd3a2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -195,7 +195,8 @@
     WEAR,
     GLANCE,
     TOOLS,
-    KMP,
+    KMP, // All projects built as Kotlin Multi Platform (compose, datastore, collections, etc).
+    INFRAROGUE, // Projects built by playground team, mostly non-compose kmp.
     CAMERA,
     NATIVE,
     WINDOW,
@@ -255,6 +256,9 @@
             case "KMP":
                 filter.add(BuildType.KMP)
                 break
+            case "INFRAROGUE":
+                filter.add(BuildType.INFRAROGUE)
+                break
             case "CAMERA":
                 filter.add(BuildType.CAMERA)
                 break
@@ -449,10 +453,10 @@
 includeProject(":autofill:autofill", [BuildType.MAIN])
 includeProject(":benchmark:benchmark-benchmark", "benchmark/benchmark", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":benchmark:benchmark-common")
-includeProject(":benchmark:benchmark-darwin", [BuildType.KMP])
-includeProject(":benchmark:benchmark-darwin-core", [BuildType.KMP])
-includeProject(":benchmark:benchmark-darwin-samples", [BuildType.KMP])
-includeProject(":benchmark:benchmark-darwin-gradle-plugin", [BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin", [BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-core", [BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-samples", [BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-gradle-plugin", [BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":benchmark:benchmark-gradle-plugin", "benchmark/gradle-plugin", [BuildType.MAIN])
 includeProject(":benchmark:benchmark-baseline-profile-gradle-plugin", "benchmark/baseline-profile-gradle-plugin",[BuildType.MAIN])
 includeProject(":benchmark:benchmark-junit4")
@@ -521,11 +525,11 @@
 includeProject(":car:app:app-samples:showcase-mobile", "car/app/app-samples/showcase/mobile", [BuildType.MAIN])
 includeProject(":car:app:app-testing", [BuildType.MAIN])
 includeProject(":cardview:cardview", [BuildType.MAIN])
-includeProject(":collection:collection", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:collection-benchmark", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:collection-benchmark-kmp", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:collection-ktx", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:integration-tests:testapp", [BuildType.MAIN, BuildType.KMP])
+includeProject(":collection:collection", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:collection-benchmark", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:collection-benchmark-kmp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:collection-ktx", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:integration-tests:testapp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":compose:animation", [BuildType.COMPOSE])
 includeProject(":compose:animation:animation", [BuildType.COMPOSE])
 includeProject(":compose:animation:animation-lint", [BuildType.COMPOSE])
@@ -549,9 +553,9 @@
 includeProject(":compose:compiler:compiler-daemon:integration-tests", [BuildType.COMPOSE])
 
 if (isMultiplatformEnabled()) {
-    includeProject(":compose:desktop", [BuildType.COMPOSE])
-    includeProject(":compose:desktop:desktop", [BuildType.COMPOSE])
-    includeProject(":compose:desktop:desktop:desktop-samples", "compose/desktop/desktop/samples", [BuildType.COMPOSE])
+    includeProject(":compose:desktop", [BuildType.COMPOSE, BuildType.KMP])
+    includeProject(":compose:desktop:desktop", [BuildType.COMPOSE, BuildType.KMP])
+    includeProject(":compose:desktop:desktop:desktop-samples", "compose/desktop/desktop/samples", [BuildType.COMPOSE, BuildType.KMP])
 }
 includeProject(":compose:foundation", [BuildType.COMPOSE])
 includeProject(":compose:foundation:foundation", [BuildType.COMPOSE])
@@ -601,7 +605,7 @@
 includeProject(":compose:material:material:material-samples", "compose/material/material/samples", [BuildType.COMPOSE])
 includeProject(":compose:material3:material3:material3-samples", "compose/material3/material3/samples", [BuildType.COMPOSE])
 includeProject(":compose:runtime", [BuildType.COMPOSE])
-includeProject(":compose:runtime:runtime", [BuildType.COMPOSE])
+includeProject(":compose:runtime:runtime", [BuildType.COMPOSE, BuildType.KMP])
 includeProject(":compose:runtime:runtime-lint", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-livedata", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-livedata:runtime-livedata-samples", "compose/runtime/runtime-livedata/samples", [BuildType.COMPOSE])
@@ -687,20 +691,20 @@
 includeProject(":cursoradapter:cursoradapter", [BuildType.MAIN])
 includeProject(":customview:customview", [BuildType.MAIN])
 includeProject(":customview:customview-poolingcontainer", [BuildType.MAIN, BuildType.COMPOSE])
-includeProject(":datastore:datastore", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-benchmark", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-core", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-core-okio", [BuildType.MAIN, BuildType.KMP])
+includeProject(":datastore:datastore", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-benchmark", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-core", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-core-okio", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":datastore:datastore-compose-samples", [BuildType.COMPOSE])
-includeProject(":datastore:datastore-preferences", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-core", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-proto", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-rxjava2", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-rxjava3", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-proto", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-rxjava2", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-rxjava3", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-sampleapp", [BuildType.MAIN, BuildType.KMP])
+includeProject(":datastore:datastore-preferences", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-core", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-proto", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-proto", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-sampleapp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":documentfile:documentfile", [BuildType.MAIN])
 includeProject(":draganddrop:draganddrop", [BuildType.MAIN])
 includeProject(":draganddrop:integration-tests:sampleapp", [BuildType.MAIN])
@@ -1119,14 +1123,14 @@
 /////////////////////////////
 
 includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
-includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN, BuildType.KMP])
-includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA])
+includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA, BuildType.WEAR])
 includeProject(":internal-testutils-appcompat", "testutils/testutils-appcompat", [BuildType.MAIN])
 includeProject(":internal-testutils-espresso", "testutils/testutils-espresso", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-fonts", "testutils/testutils-fonts", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":internal-testutils-truth", "testutils/testutils-truth")
 includeProject(":internal-testutils-ktx", "testutils/testutils-ktx")
-includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, BuildType.KMP, BuildType.COMPOSE])
+includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP, BuildType.COMPOSE])
 includeProject(":internal-testutils-macrobenchmark", "testutils/testutils-macrobenchmark", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-navigation", "testutils/testutils-navigation", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
 includeProject(":internal-testutils-paging", "testutils/testutils-paging", [BuildType.MAIN, BuildType.COMPOSE])
@@ -1180,7 +1184,7 @@
     includeProject(":docs-public")
 }
 
-includeProject(":docs-kmp", [BuildType.KMP])
+includeProject(":docs-kmp", [BuildType.KMP, BuildType.INFRAROGUE])
 // placeholder test project that has a test for each size to ensure that at least one test is run
 // for each size and test runner is happy when there is nothing to test.
 includeProject(":placeholder-tests")
diff --git a/slice/slice-view/src/main/res/values-da/strings.xml b/slice/slice-view/src/main/res/values-da/strings.xml
index 1491d22..379bfdb 100644
--- a/slice/slice-view/src/main/res/values-da/strings.xml
+++ b/slice/slice-view/src/main/res/values-da/strings.xml
@@ -30,8 +30,8 @@
       <item quantity="other">For <xliff:g id="ID_2">%d</xliff:g> år siden</item>
     </plurals>
     <plurals name="abc_slice_duration_days" formatted="false" msgid="8356547162075064530">
-      <item quantity="one">For <xliff:g id="ID_2">%d</xliff:g> dag siden</item>
-      <item quantity="other">For <xliff:g id="ID_2">%d</xliff:g> dage siden</item>
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> dag siden</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dage siden</item>
     </plurals>
     <string name="abc_slice_error" msgid="1794214973158263497">"Der kunne ikke oprettes forbindelse"</string>
 </resources>
diff --git a/startup/integration-tests/test-app/build.gradle b/startup/integration-tests/test-app/build.gradle
index 7dbe4b8..93f6d4d 100644
--- a/startup/integration-tests/test-app/build.gradle
+++ b/startup/integration-tests/test-app/build.gradle
@@ -35,6 +35,6 @@
     implementation(project(":startup:integration-tests:first-library"))
     implementation(project(":startup:integration-tests:second-library"))
     implementation(libs.constraintLayout)
-    implementation("androidx.arch.core:core-runtime:2.1.0")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation("androidx.appcompat:appcompat:1.2.0")
 }
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
index 02496f3..fc177b9 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
@@ -59,12 +59,17 @@
     const val HYPHENATION_FREQUENCY_NONE = Layout.HYPHENATION_FREQUENCY_NONE
     const val HYPHENATION_FREQUENCY_NORMAL = Layout.HYPHENATION_FREQUENCY_NORMAL
     const val HYPHENATION_FREQUENCY_NORMAL_FAST = Layout.HYPHENATION_FREQUENCY_NORMAL_FAST
+    const val HYPHENATION_FREQUENCY_FULL = Layout.HYPHENATION_FREQUENCY_FULL
+    const val HYPHENATION_FREQUENCY_FULL_FAST = Layout.HYPHENATION_FREQUENCY_FULL_FAST
 
     @Retention(AnnotationRetention.SOURCE)
     @IntDef(
+        HYPHENATION_FREQUENCY_NONE,
         HYPHENATION_FREQUENCY_NORMAL,
         HYPHENATION_FREQUENCY_NORMAL_FAST,
-        HYPHENATION_FREQUENCY_NONE
+        HYPHENATION_FREQUENCY_FULL,
+        HYPHENATION_FREQUENCY_FULL_FAST
+
     )
     internal annotation class HyphenationFrequency
 
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
index 6f6d535..855ac02 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
@@ -16,6 +16,13 @@
 
 package androidx.tv.integration.playground
 
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -88,7 +95,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
 @Composable
 internal fun FeaturedCarousel(modifier: Modifier = Modifier) {
     val backgrounds = listOf(
@@ -117,33 +124,31 @@
                     .align(Alignment.BottomEnd)
                     .padding(16.dp),
             )
-        }
+        },
+        contentTransformStartToEnd =
+            fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+        contentTransformEndToStart =
+            fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
     ) { itemIndex ->
-        CarouselItem(
-            modifier = Modifier.semantics {
-                contentDescription = "Featured Content"
-            },
-            background = {
-                Box(
-                    modifier = Modifier
-                        .background(backgrounds[itemIndex])
-                        .fillMaxSize()
-                )
-            },
+        Box(
+            modifier = Modifier
+                .background(backgrounds[itemIndex])
+                .fillMaxSize()
+                .semantics { contentDescription = "Featured Content" }
         ) {
-            Box(
+            Column(
                 modifier = Modifier
-                    .fillMaxSize()
-                    .padding(20.dp),
-                contentAlignment = Alignment.BottomStart
+                    .padding(start = 50.dp, top = 100.dp)
+                    .animateEnterExit(
+                        enter = slideInVertically(animationSpec = tween(1000)),
+                        exit = slideOutHorizontally(animationSpec = tween(1000))
+                    )
             ) {
-                Column {
-                    Text(text = "This is sample text content.", color = Color.Yellow)
-                    Text(text = "Sample description.", color = Color.Yellow)
-                    Row {
-                        OverlayButton(text = "Play")
-                        OverlayButton(text = "Add to Watchlist")
-                    }
+                Text(text = "This is sample text content.", color = Color.Yellow)
+                Text(text = "Sample description of slide ${itemIndex + 1}.", color = Color.Yellow)
+                Row {
+                    OverlayButton(text = "Play")
+                    OverlayButton(text = "Add to Watchlist")
                 }
             }
         }
diff --git a/tv/integration-tests/presentation/README.md b/tv/integration-tests/presentation/README.md
index eb86b24..9c46867 100644
--- a/tv/integration-tests/presentation/README.md
+++ b/tv/integration-tests/presentation/README.md
@@ -3,12 +3,11 @@
 ## Setup
 
 * Uncomment the `coil` and `gson` libraries dependency additions from the `build.gradle` file.
+* Uncomment the function content and imports from
+  `presentation/src/main/java/androidx/tv/integration/presentation/ExternalLibs.kt` file
 * Create the `data.json` file in `presentation/src/main/assets` directory and add the content from
   this link: go/compose-tv-presentation-app-data
 
 > If you are not a Googler and want to use this app for
 > testing, you will have to create the `data.json` file by following the schema mentioned in the
 `Data.kt` file
-
-* Uncomment the function content and imports from
-  `presentation/src/main/java/androidx/tv/integration/presentation/ExternalLibs.kt` file
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
index 33e5b16..94df400 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
@@ -16,6 +16,15 @@
 
 package androidx.tv.integration.presentation
 
+import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -32,7 +41,6 @@
 import androidx.compose.ui.unit.sp
 import androidx.tv.material3.Carousel
 import androidx.tv.material3.CarouselDefaults
-import androidx.tv.material3.CarouselScope
 import androidx.tv.material3.CarouselState
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Text
@@ -60,7 +68,11 @@
                     .align(Alignment.BottomEnd)
                     .padding(end = 58.dp, bottom = 16.dp),
             )
-        }
+        },
+        contentTransformEndToStart =
+            fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+        contentTransformStartToEnd =
+            fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
     ) { itemIndex ->
         val movie = movies[itemIndex]
 
@@ -80,22 +92,23 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalAnimationApi::class)
 @Composable
-private fun CarouselScope.CarouselSlide(
+private fun AnimatedContentScope.CarouselSlide(
     title: String,
     description: String,
     background: @Composable () -> Unit,
     actions: @Composable () -> Unit
 ) {
-    CarouselItem(
-        background = {
-            background()
-        },
-        modifier = Modifier
-    ) {
+    Box {
+        background()
         Column(
-            modifier = Modifier.padding(start = 58.dp, top = 150.dp)
+            modifier = Modifier
+                .padding(start = 58.dp, top = 150.dp)
+                .animateEnterExit(
+                    enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
+                    exit = slideOutHorizontally(animationSpec = tween(1000))
+                )
         ) {
             Text(
                 text = title,
diff --git a/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
index aba7109..b356829 100644
--- a/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
+++ b/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
@@ -17,6 +17,13 @@
 package androidx.tv.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Box
@@ -44,7 +51,7 @@
 import androidx.tv.material3.CarouselState
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
 @Sampled
 @Composable
 fun SimpleCarousel() {
@@ -59,16 +66,16 @@
         modifier = Modifier
             .height(300.dp)
             .fillMaxWidth(),
+        contentTransformEndToStart =
+        fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+        contentTransformStartToEnd =
+        fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
     ) { itemIndex ->
-        CarouselItem(
-            background = {
-                Box(
-                    modifier = Modifier
-                        .background(backgrounds[itemIndex])
-                        .border(2.dp, Color.White.copy(alpha = 0.5f))
-                        .fillMaxSize()
-                )
-            }
+        Box(
+            modifier = Modifier
+                .background(backgrounds[itemIndex])
+                .border(2.dp, Color.White.copy(alpha = 0.5f))
+                .fillMaxSize()
         ) {
             var isFocused by remember { mutableStateOf(false) }
 
@@ -82,6 +89,13 @@
                         color = if (isFocused) Color.Red else Color.Transparent,
                         shape = RoundedCornerShape(50)
                     )
+                    // Duration of animation here should be less than or equal to carousel's
+                    // contentTransform duration to ensure the item below does not disappear
+                    // abruptly.
+                    .animateEnterExit(
+                        enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
+                        exit = slideOutHorizontally(animationSpec = tween(1000))
+                    )
                     .padding(vertical = 2.dp, horizontal = 5.dp)
             ) {
                 Text(text = "Play")
@@ -90,7 +104,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
 @Sampled
 @Composable
 fun CarouselIndicatorWithRectangleShape() {
@@ -127,24 +141,30 @@
                     )
                 }
             )
-        }
+        },
+        contentTransformEndToStart =
+        fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+        contentTransformStartToEnd =
+        fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
     ) { itemIndex ->
-        CarouselItem(
-            background = {
-                Box(
-                    modifier = Modifier
-                        .background(backgrounds[itemIndex])
-                        .border(2.dp, Color.White.copy(alpha = 0.5f))
-                        .fillMaxSize()
-                )
-            }
+        Box(
+            modifier = Modifier
+                .background(backgrounds[itemIndex])
+                .border(2.dp, Color.White.copy(alpha = 0.5f))
+                .fillMaxSize()
         ) {
             var isFocused by remember { mutableStateOf(false) }
-
             Button(
                 onClick = { },
                 modifier = Modifier
                     .onFocusChanged { isFocused = it.isFocused }
+                    // Duration of animation here should be less than or equal to carousel's
+                    // contentTransform duration to ensure the item below does not disappear
+                    // abruptly.
+                    .animateEnterExit(
+                        enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
+                        exit = slideOutHorizontally(animationSpec = tween(1000))
+                    )
                     .padding(40.dp)
                     .border(
                         width = 2.dp,
diff --git a/tv/tv-foundation/build.gradle b/tv/tv-foundation/build.gradle
index c193d0d..a53a44ee 100644
--- a/tv/tv-foundation/build.gradle
+++ b/tv/tv-foundation/build.gradle
@@ -30,7 +30,7 @@
 dependencies {
     api(libs.kotlinStdlib)
 
-    def composeVersion = '1.4.0-rc01'
+    def composeVersion = '1.4.2'
 
     implementation(libs.kotlinStdlibCommon)
     implementation("androidx.profileinstaller:profileinstaller:1.3.0")
diff --git a/tv/tv-material/api/public_plus_experimental_current.txt b/tv/tv-material/api/public_plus_experimental_current.txt
index 12488a4..093044e 100644
--- a/tv/tv-material/api/public_plus_experimental_current.txt
+++ b/tv/tv-material/api/public_plus_experimental_current.txt
@@ -114,24 +114,8 @@
     field public static final long TimeToDisplayItemMillis = 5000L; // 0x1388L
   }
 
-  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselItemDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformEndToStart();
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformLeftToRight();
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformRightToLeft();
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformStartToEnd();
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformEndToStart;
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformLeftToRight;
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformRightToLeft;
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformStartToEnd;
-    field public static final androidx.tv.material3.CarouselItemDefaults INSTANCE;
-  }
-
   public final class CarouselKt {
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Carousel(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CarouselState carouselState, optional long autoScrollDurationMillis, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> carouselIndicator, kotlin.jvm.functions.Function2<? super androidx.tv.material3.CarouselScope,? super java.lang.Integer,kotlin.Unit> content);
-  }
-
-  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselScope {
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void CarouselItem(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> background, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Carousel(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CarouselState carouselState, optional long autoScrollDurationMillis, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> carouselIndicator, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super java.lang.Integer,kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselState {
diff --git a/tv/tv-material/build.gradle b/tv/tv-material/build.gradle
index 0b86754..334a3f1 100644
--- a/tv/tv-material/build.gradle
+++ b/tv/tv-material/build.gradle
@@ -27,19 +27,10 @@
 dependencies {
     api(libs.kotlinStdlib)
 
-    def composeVersion = '1.4.0-rc01'
-
-    api("androidx.annotation:annotation:1.5.0")
-    api("androidx.compose.runtime:runtime:$composeVersion")
+    def composeVersion = '1.4.2'
 
     api(project(":compose:animation:animation"))
-    api("androidx.compose.ui:ui:$composeVersion")
-    api("androidx.compose.foundation:foundation:$composeVersion")
-    api("androidx.compose.foundation:foundation-layout:$composeVersion")
     api("androidx.compose.material:material-icons-core:$composeVersion")
-    api("androidx.compose.ui:ui-graphics:$composeVersion")
-    api(project(":compose:ui:ui-text"))
-    api("androidx.compose.ui:ui-util:$composeVersion")
     api(project(":tv:tv-foundation"))
 
     implementation(libs.kotlinStdlibCommon)
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
deleted file mode 100644
index 2f80311..0000000
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2023 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.tv.material3
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.text.BasicText
-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.Modifier
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.key.NativeKeyEvent
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.SemanticsActions
-import androidx.compose.ui.test.assertIsFocused
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.performSemanticsAction
-import androidx.compose.ui.unit.dp
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Rule
-import org.junit.Test
-
-const val sampleButtonTag = "sample-button"
-
-class CarouselScopeTest {
-    @get:Rule
-    val rule = createComposeRule()
-
-    @OptIn(ExperimentalTvMaterial3Api::class)
-    @Test
-    fun carouselItem_parentContainerGainsFocused_onBackPress() {
-        val containerBoxTag = "container-box"
-        val carouselItemTag = "carousel-item"
-
-        rule.setContent {
-            val carouselState = remember { CarouselState() }
-            var isContainerBoxFocused by remember { mutableStateOf(false) }
-            Box(
-                modifier = Modifier
-                    .testTag(containerBoxTag)
-                    .fillMaxSize()
-                    .onFocusChanged { isContainerBoxFocused = it.isFocused }
-                    .border(10.dp, if (isContainerBoxFocused) Color.Green else Color.Transparent)
-                    .focusable()
-            ) {
-                CarouselScope(carouselState = carouselState)
-                    .CarouselItem(
-                        modifier = Modifier
-                            .testTag(carouselItemTag),
-                        background = {
-                            Box(
-                                modifier = Modifier
-                                    .size(300.dp)
-                                    .background(Color.Cyan))
-                        },
-                        content = { SampleButton() },
-                    )
-            }
-        }
-
-        // Request focus for Carousel Item on start
-        rule.onNodeWithTag(carouselItemTag)
-            .performSemanticsAction(SemanticsActions.RequestFocus)
-        rule.waitForIdle()
-
-        // Check if overlay button in carousel item is focused
-        rule.onNodeWithTag(sampleButtonTag, useUnmergedTree = true)
-            .assertIsFocused()
-
-        // Trigger back press
-        performKeyPress(NativeKeyEvent.KEYCODE_BACK)
-        rule.waitForIdle()
-
-        // Check if carousel item loses focus and parent container gains focus
-        rule.onNodeWithTag(containerBoxTag).assertIsFocused()
-    }
-
-    private fun performKeyPress(keyCode: Int, count: Int = 1) {
-        repeat(count) {
-            InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode)
-        }
-    }
-}
-
-@Composable
-private fun SampleButton(text: String = sampleButtonTag) {
-    var isFocused by remember { mutableStateOf(false) }
-    BasicText(
-        text = text,
-        modifier = Modifier
-            .testTag(text)
-            .size(100.dp, 20.dp)
-            .background(Color.Yellow)
-            .onFocusChanged { isFocused = it.isFocused }
-            .border(2.dp, if (isFocused) Color.Green else Color.Transparent)
-            .focusable()
-    )
-}
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
index 1c863ef..b6c31ca8 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
@@ -18,8 +18,10 @@
 
 import android.os.SystemClock
 import android.view.KeyEvent
-import androidx.compose.animation.ContentTransform
+import androidx.compose.animation.AnimatedContentScope
 import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -75,7 +77,7 @@
 private const val delayBetweenItems = 2500L
 private const val animationTime = 900L
 
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
 class CarouselTest {
     @get:Rule
     val rule = createComposeRule()
@@ -433,7 +435,13 @@
                         autoScrollDurationMillis = delayBetweenItems
                     ) {
                         SampleCarouselItem(index = it) {
-                            Box {
+                            Box(
+                                modifier = Modifier
+                                    .animateEnterExit(
+                                        enter = slideInHorizontally(),
+                                        exit = slideOutHorizontally()
+                                    )
+                            ) {
                                 Column(modifier = Modifier.align(Alignment.BottomStart)) {
                                     BasicText(text = "carousel-frame")
                                     Row {
@@ -829,7 +837,7 @@
     carouselState: CarouselState = remember { CarouselState() },
     itemCount: Int = 3,
     timeToDisplayItemMillis: Long = delayBetweenItems,
-    content: @Composable CarouselScope.(index: Int) -> Unit
+    content: @Composable AnimatedContentScope.(index: Int) -> Unit
 ) {
     Carousel(
         modifier = Modifier
@@ -856,24 +864,16 @@
 
 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
 @Composable
-private fun CarouselScope.SampleCarouselItem(
+private fun AnimatedContentScope.SampleCarouselItem(
     index: Int,
     modifier: Modifier = Modifier,
-    contentTransformStartToEnd: ContentTransform =
-        CarouselItemDefaults.contentTransformStartToEnd,
-    content: (@Composable () -> Unit) = { SampleButton("Play $index") },
+    content: (@Composable AnimatedContentScope.() -> Unit) = { SampleButton("Play $index") },
 ) {
-    CarouselItem(
-        modifier = modifier,
-        contentTransformStartToEnd = contentTransformStartToEnd,
-        background = {
-            Box(
-                modifier = Modifier
-                    .fillMaxSize()
-                    .background(Color.Red)
-                    .border(2.dp, Color.Blue)
-            )
-        }
+    Box(
+        modifier = modifier
+            .fillMaxSize()
+            .background(Color.Red)
+            .border(2.dp, Color.Blue)
     ) {
         content()
     }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index c8b28a0..001aed4 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -17,6 +17,7 @@
 package androidx.tv.material3
 
 import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.AnimatedContentScope
 import androidx.compose.animation.AnimatedVisibilityScope
 import androidx.compose.animation.ContentTransform
 import androidx.compose.animation.ExperimentalAnimationApi
@@ -113,7 +114,7 @@
                 .padding(16.dp),
         )
     },
-    content: @Composable CarouselScope.(index: Int) -> Unit
+    content: @Composable AnimatedContentScope.(index: Int) -> Unit
 ) {
     CarouselStateUpdater(carouselState, itemCount)
     var focusState: FocusState? by remember { mutableStateOf(null) }
@@ -176,8 +177,7 @@
             // IndexOutOfBoundsException. Guarding against this by checking against itemCount
             // before invoking.
             if (itemCount > 0) {
-                CarouselScope(carouselState = carouselState)
-                    .content(if (activeItemIndex < itemCount) activeItemIndex else 0)
+                content(if (activeItemIndex < itemCount) activeItemIndex else 0)
             }
         }
         this.carouselIndicator()
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
deleted file mode 100644
index d0b896a..0000000
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2023 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.tv.material3
-
-import android.content.Context
-import android.view.accessibility.AccessibilityManager
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.ContentTransform
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.animation.slideInHorizontally
-import androidx.compose.animation.slideOutHorizontally
-import androidx.compose.animation.togetherWith
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection
-import androidx.compose.ui.focus.FocusState
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.semantics.CollectionItemInfo
-import androidx.compose.ui.semantics.collectionItemInfo
-import androidx.compose.ui.semantics.isContainer
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.tv.material3.KeyEventPropagation.ContinuePropagation
-
-/**
- * This composable is intended for use in Carousel.
- * A composable that has
- * - a [background] layer that is rendered as soon as the composable is visible.
- * - a [content] layer that is rendered on top of the [background]
- *
- * @param background composable defining the background of the item
- * @param itemIndex current active item index of the carousel
- * @param modifier modifier applied to the CarouselItem
- * @param contentTransform content transform to be applied to the content of the item when
- * scrolling
- * @param content composable defining the content displayed on top of the background
- */
-@Suppress("IllegalExperimentalApiUsage")
-@OptIn(ExperimentalAnimationApi::class, ExperimentalComposeUiApi::class)
-@ExperimentalTvMaterial3Api
-@Composable
-internal fun CarouselItem(
-    itemIndex: Int,
-    modifier: Modifier = Modifier,
-    background: @Composable () -> Unit = {},
-    contentTransform: ContentTransform =
-        CarouselItemDefaults.contentTransformStartToEnd,
-    content: @Composable () -> Unit,
-) {
-    val context = LocalContext.current
-    val accessibilityManager = remember {
-        context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
-    }
-    var containerBoxFocusState: FocusState? by remember { mutableStateOf(null) }
-    val focusManager = LocalFocusManager.current
-    var exitFocus by remember { mutableStateOf(false) }
-
-    var isVisible by remember { mutableStateOf(false) }
-
-    DisposableEffect(itemIndex) {
-        isVisible = true
-        onDispose { isVisible = false }
-    }
-
-    // This box holds the focus until the overlay animation completes
-    Box(
-        modifier = modifier
-            .semantics(mergeDescendants = true) {
-                @Suppress("DEPRECATION")
-                isContainer = true
-                collectionItemInfo =
-                    CollectionItemInfo(
-                        rowIndex = 0,
-                        rowSpan = 1,
-                        columnIndex = itemIndex,
-                        columnSpan = 1
-                    )
-            }
-            .onKeyEvent {
-                exitFocus = it.isBackPress() && it.isTypeKeyDown()
-                ContinuePropagation
-            }
-            .onFocusChanged {
-                containerBoxFocusState = it
-                if (it.isFocused && exitFocus) {
-                    focusManager.moveFocus(FocusDirection.Exit)
-                    exitFocus = false
-                }
-            }
-            .then(
-                if (accessibilityManager.isEnabled)
-                    Modifier.clickable {
-                        focusManager.moveFocus(FocusDirection.Enter)
-                    }
-                else
-                    Modifier.focusable()
-            )
-    ) {
-        background()
-
-        AnimatedVisibility(
-            visible = isVisible,
-            enter = contentTransform.targetContentEnter,
-            exit = contentTransform.initialContentExit,
-        ) {
-            LaunchedEffect(transition.isRunning, containerBoxFocusState?.isFocused) {
-                if (!transition.isRunning &&
-                    containerBoxFocusState?.isFocused == true &&
-                    !accessibilityManager.isEnabled
-                ) {
-                    focusManager.moveFocus(FocusDirection.Enter)
-                }
-            }
-            content.invoke()
-        }
-    }
-}
-
-@ExperimentalTvMaterial3Api
-object CarouselItemDefaults {
-    /**
-     * Transform the content from right to left
-     */
-    // Keeping this as public so that users can access it directly without the isLTR helper
-    val contentTransformRightToLeft: ContentTransform
-        @Composable get() =
-            slideInHorizontally { it * 4 }
-                .togetherWith(slideOutHorizontally { it * 4 })
-
-    /**
-     * Transform the content from left to right
-     */
-    // Keeping this as public so that users can access it directly without the isLTR helper
-    val contentTransformLeftToRight: ContentTransform
-        @Composable get() =
-            slideInHorizontally()
-                .togetherWith(slideOutHorizontally())
-
-    /**
-     * Content transform applied when moving forward taking isLTR into account
-     */
-    val contentTransformStartToEnd
-        @Composable get() =
-            if (isLtr())
-                contentTransformRightToLeft
-            else
-                contentTransformLeftToRight
-
-    /**
-     * Content transform applied when moving backward taking isLTR into account
-     */
-    val contentTransformEndToStart
-        @Composable get() =
-            if (isLtr())
-                contentTransformLeftToRight
-            else
-                contentTransformRightToLeft
-}
-
-@Composable
-private fun isLtr() = LocalLayoutDirection.current == LayoutDirection.Ltr
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
deleted file mode 100644
index 665ceb9..0000000
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2023 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.tv.material3
-
-import androidx.compose.animation.ContentTransform
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-/**
- * CarouselScope provides a [CarouselScope.CarouselItem] function which you can use to
- * provide the carousel item's animation, background and the inner content.
- */
-@ExperimentalTvMaterial3Api
-class CarouselScope @OptIn(ExperimentalTvMaterial3Api::class)
-internal constructor(private val carouselState: CarouselState) {
-    /**
-     * [CarouselScope.CarouselItem] can be used to define a item's animation, background, and
-     * content. Using this is optional and you can choose to define your own CarouselItem from
-     * scratch
-     *
-     * @param modifier modifier applied to the CarouselItem
-     * @param background composable defining the background of the item
-     * @param contentTransformStartToEnd content transform to be applied to the content of the item
-     * when scrolling forward in the carousel
-     * @param contentTransformEndToStart content transform to be applied to the content of the item
-     * when scrolling backward in the carousel
-     * @param content composable defining the content displayed on top of the background
-     */
-    @Composable
-    @ExperimentalTvMaterial3Api
-    fun CarouselItem(
-        modifier: Modifier = Modifier,
-        background: @Composable () -> Unit = {},
-        contentTransformStartToEnd: ContentTransform =
-            CarouselItemDefaults.contentTransformStartToEnd,
-        contentTransformEndToStart: ContentTransform =
-            CarouselItemDefaults.contentTransformEndToStart,
-        content: @Composable () -> Unit
-    ) {
-        CarouselItem(
-            background = background,
-            itemIndex = carouselState.activeItemIndex,
-            contentTransform =
-            if (carouselState.isMovingBackward)
-                contentTransformEndToStart
-            else
-                contentTransformStartToEnd,
-            modifier = modifier,
-            content = content,
-        )
-    }
-}
diff --git a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt
index c258ffa..2b96e52 100644
--- a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt
+++ b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt
@@ -99,23 +99,20 @@
     onTextLayout: (TextLayoutResult) -> Unit,
     style: TextStyle
 ) {
-    val mergedStyle = style.merge(
-        TextStyle(
-            color = color,
-            fontSize = fontSize,
-            fontWeight = fontWeight,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            fontFamily = fontFamily,
-            textDecoration = textDecoration,
-            fontStyle = fontStyle,
-            letterSpacing = letterSpacing
-        )
-    )
     BasicText(
         text = text,
         modifier = modifier,
-        style = mergedStyle,
+        style = style.merge(
+                color = color,
+                fontSize = fontSize,
+                fontWeight = fontWeight,
+                textAlign = textAlign,
+                lineHeight = lineHeight,
+                fontFamily = fontFamily,
+                textDecoration = textDecoration,
+                fontStyle = fontStyle,
+                letterSpacing = letterSpacing
+        ),
         onTextLayout = onTextLayout,
         overflow = overflow,
         softWrap = softWrap,
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 208a7d4..d70955a 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -939,20 +939,6 @@
     field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
   }
 
-  public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
-    method public int getAnimatedImageFormat();
-    method @DrawableRes public int getResourceId();
-    method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
-  }
-
-  public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder {
-    ctor public ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId build();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
-  }
-
   public static final class ResourceBuilders.AndroidImageResourceByResId {
     method @DrawableRes public int getResourceId();
   }
@@ -963,33 +949,15 @@
     method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
   }
 
-  public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
-    method public int getAnimatedImageFormat();
-    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
-    method @DrawableRes public int getResourceId();
-  }
-
-  public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder {
-    ctor public ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId build();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
-  }
-
   public static final class ResourceBuilders.ImageResource {
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
     method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
     method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
   }
 
   public static final class ResourceBuilders.ImageResource.Builder {
     ctor public ResourceBuilders.ImageResource.Builder();
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
-    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
-    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
   }
 
diff --git a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
index 4a1e57f..9660d46 100644
--- a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
@@ -1125,7 +1125,7 @@
     field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
   }
 
-  public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
+  @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
     method public int getAnimatedImageFormat();
     method @DrawableRes public int getResourceId();
     method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
@@ -1149,7 +1149,7 @@
     method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
   }
 
-  public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
+  @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
     method public int getAnimatedImageFormat();
     method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
     method @DrawableRes public int getResourceId();
@@ -1164,18 +1164,18 @@
   }
 
   public static final class ResourceBuilders.ImageResource {
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
     method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
     method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
   }
 
   public static final class ResourceBuilders.ImageResource.Builder {
     ctor public ResourceBuilders.ImageResource.Builder();
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
-    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
-    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
   }
 
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 208a7d4..d70955a 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -939,20 +939,6 @@
     field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
   }
 
-  public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
-    method public int getAnimatedImageFormat();
-    method @DrawableRes public int getResourceId();
-    method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
-  }
-
-  public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder {
-    ctor public ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId build();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
-  }
-
   public static final class ResourceBuilders.AndroidImageResourceByResId {
     method @DrawableRes public int getResourceId();
   }
@@ -963,33 +949,15 @@
     method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
   }
 
-  public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
-    method public int getAnimatedImageFormat();
-    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
-    method @DrawableRes public int getResourceId();
-  }
-
-  public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder {
-    ctor public ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId build();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
-  }
-
   public static final class ResourceBuilders.ImageResource {
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
     method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
-    method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
     method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
   }
 
   public static final class ResourceBuilders.ImageResource.Builder {
     ctor public ResourceBuilders.ImageResource.Builder();
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
-    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
-    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
     method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
   }
 
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
index f552d96..2fa280a 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
@@ -25,11 +25,13 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.protolayout.TriggerBuilders.Trigger;
 import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat;
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
 import androidx.wear.protolayout.proto.ResourceProto;
 import androidx.wear.protolayout.protobuf.ByteString;
 
@@ -333,6 +335,7 @@
      *
      * @since 1.2
      */
+    @ProtoLayoutExperimental
     public static final class AndroidAnimatedImageResourceByResId {
         private final ResourceProto.AndroidAnimatedImageResourceByResId mImpl;
 
@@ -463,6 +466,7 @@
      *
      * @since 1.2
      */
+    @ProtoLayoutExperimental
     public static final class AndroidSeekableAnimatedImageResourceByResId {
         private final ResourceProto.AndroidSeekableAnimatedImageResourceByResId mImpl;
 
@@ -649,6 +653,7 @@
          * @since 1.2
          */
         @Nullable
+        @ProtoLayoutExperimental
         public AndroidAnimatedImageResourceByResId getAndroidAnimatedResourceByResId() {
             if (mImpl.hasAndroidAnimatedResourceByResId()) {
                 return AndroidAnimatedImageResourceByResId.fromProto(
@@ -665,6 +670,7 @@
          * @since 1.2
          */
         @Nullable
+        @ProtoLayoutExperimental
         public AndroidSeekableAnimatedImageResourceByResId
                 getAndroidSeekableAnimatedResourceByResId() {
             if (mImpl.hasAndroidSeekableAnimatedResourceByResId()) {
@@ -696,6 +702,7 @@
 
         @Override
         @NonNull
+        @OptIn(markerClass = ProtoLayoutExperimental.class)
         public String toString() {
             return "ImageResource{"
                     + "androidResourceByResId="
@@ -746,6 +753,7 @@
              * @since 1.2
              */
             @NonNull
+            @ProtoLayoutExperimental
             public Builder setAndroidAnimatedResourceByResId(
                     @NonNull AndroidAnimatedImageResourceByResId androidAnimatedResourceByResId) {
                 mImpl.setAndroidAnimatedResourceByResId(androidAnimatedResourceByResId.toProto());
@@ -759,6 +767,7 @@
              * @since 1.2
              */
             @NonNull
+            @ProtoLayoutExperimental
             public Builder setAndroidSeekableAnimatedResourceByResId(
                     @NonNull
                             AndroidSeekableAnimatedImageResourceByResId
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
index 8a04d54..307c757 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
@@ -118,8 +118,7 @@
 
     /** Whether or not the watch face supports [renderWatchFaceToSurface]. */
     public val isRenderWatchFaceToSurfaceSupported: Boolean
-        @get:JvmName("isRenderWatchFaceToSurfaceSupported")
-        get() = false
+        @get:JvmName("isRenderWatchFaceToSurfaceSupported") get() = false
 
     /**
      * Renders the [androidx.wear.watchface.ComplicationSlot] to a shared memory backed [Bitmap]
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
index 17261be..93d8ddca 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
@@ -88,10 +88,10 @@
 }
 
 /**
- * Intended for use by watch face editors, a RemoteWatchFaceViewHost allows the watch face to send
- * a [SurfaceControlViewHost.SurfacePackage] to the client, which the client can attach to a
- * [SurfaceView] with [SurfaceView.setChildSurfacePackage]. The client can request an updated
- * screen shot by calling [renderWatchFace].
+ * Intended for use by watch face editors, a RemoteWatchFaceViewHost allows the watch face to send a
+ * [SurfaceControlViewHost.SurfacePackage] to the client, which the client can attach to a
+ * [SurfaceView] with [SurfaceView.setChildSurfacePackage]. The client can request an updated screen
+ * shot by calling [renderWatchFace].
  */
 public interface RemoteWatchFaceViewHost : AutoCloseable {
     /**
@@ -161,8 +161,7 @@
 
     /** Whether or not the watch face supports [RemoteWatchFaceViewHost]. */
     public val isRemoteWatchFaceViewHostSupported: Boolean
-        @get:JvmName("isRemoteWatchFaceViewHostSupported")
-        get() = false
+        @get:JvmName("isRemoteWatchFaceViewHostSupported") get() = false
 
     /**
      * Constructs a [RemoteWatchFaceViewHost] whose [RemoteWatchFaceViewHost.surfacePackage] can be
@@ -180,7 +179,7 @@
      * @param width The width of the view in pixels
      * @param height The height of the view in pixels
      * @return The [RemoteWatchFaceViewHost] or null if the client has already been closed or if the
-     * watch face is not compatible.
+     *   watch face is not compatible.
      */
     @Throws(RemoteException::class)
     @RequiresApi(Build.VERSION_CODES.R)
@@ -217,9 +216,7 @@
     /**
      * Renames this instance to [newInstanceId] (must be unique, usually this would be different
      * from the old ID but that's not a requirement). Sets the current [UserStyle] represented as a
-     * [UserStyleData> and clears any complication data. Setting the new UserStyle may have a side
-     * effect of enabling or disabling complicationSlots, which will be visible via
-     * [ComplicationSlotState.isEnabled].
+     * [UserStyleData> and clears any complication data. Setting the new UserStyle may have a side effect of enabling or disabling complicationSlots, which will be visible via [ComplicationSlotState.isEnabled].
      */
     @Throws(RemoteException::class)
     public fun updateWatchFaceInstance(newInstanceId: String, userStyle: UserStyleData)
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index aba23ca..32d97b9 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -23,9 +23,9 @@
 import android.os.IBinder
 import android.os.RemoteException
 import android.util.Log
-import androidx.core.util.Consumer
 import androidx.annotation.Px
 import androidx.annotation.RestrictTo
+import androidx.core.util.Consumer
 import androidx.wear.watchface.Renderer
 import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
 import androidx.wear.watchface.complications.data.ComplicationData
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt
index 3fdeefa..9a1f229 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt
@@ -20,10 +20,7 @@
 import androidx.annotation.IntDef
 import androidx.annotation.RestrictTo
 
-/**
- * The InterruptionFilter.
- *
- */
+/** The InterruptionFilter. */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @IntDef(
     value =
diff --git a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt
index 663fb70..11e4d9c 100644
--- a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt
+++ b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt
@@ -47,4 +47,4 @@
 
         Assert.assertTrue(client.isRenderWatchFaceToSurfaceSupported)
     }
-}
\ No newline at end of file
+}
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
index 5194d6f..eb96145 100644
--- a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
@@ -48,12 +48,15 @@
                             ComplicationText.EMPTY
                         )
                         .build()
-                ComplicationType.MONOCHROMATIC_IMAGE -> MonochromaticImageComplicationData.Builder(
-                    MonochromaticImage.Builder(
-                        Icon.createWithResource(this, R.drawable.heart)
-                    ).build(),
-                    ComplicationText.EMPTY
-                ).build()
+                ComplicationType.MONOCHROMATIC_IMAGE ->
+                    MonochromaticImageComplicationData.Builder(
+                            MonochromaticImage.Builder(
+                                    Icon.createWithResource(this, R.drawable.heart)
+                                )
+                                .build(),
+                            ComplicationText.EMPTY
+                        )
+                        .build()
                 else -> null
             }
         )
@@ -67,12 +70,13 @@
             ComplicationType.LONG_TEXT ->
                 LongTextComplicationData.Builder(plainText("hello 123"), ComplicationText.EMPTY)
                     .build()
-            ComplicationType.MONOCHROMATIC_IMAGE -> MonochromaticImageComplicationData.Builder(
-                MonochromaticImage.Builder(
-                    Icon.createWithResource(this, R.drawable.heart)
-                ).build(),
-                ComplicationText.EMPTY
-            ).build()
+            ComplicationType.MONOCHROMATIC_IMAGE ->
+                MonochromaticImageComplicationData.Builder(
+                        MonochromaticImage.Builder(Icon.createWithResource(this, R.drawable.heart))
+                            .build(),
+                        ComplicationText.EMPTY
+                    )
+                    .build()
             else -> null
         }
 }
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
index daae481..8adf5fc 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
@@ -184,17 +184,13 @@
  *   android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST.
  * - A ComplicationDataSourceService must include a `meta-data` tag with
  *   android.support.wearable.complications.SUPPORTED_TYPES in its manifest entry. The value of this
- *   tag should be a comma separated list of types supported by the data source, from this table:
- * | Androidx class                       | Tag name          |
- * |--------------------------------------|-------------------|
- * | [GoalProgressComplicationData]       | GOAL_PROGRESS     |
- * | [LongTextComplicationData]           | LONG_TEXT         |
- * | [MonochromaticImageComplicationData] | ICON              |
- * | [PhotoImageComplicationData]         | LARGE_IMAGE       |
- * | [RangedValueComplicationData]        | RANGED_TEXT       |
- * | [ShortTextComplicationData]          | SHORT_TEXT        |
- * | [SmallImageComplicationData]         | SMALL_IMAGE       |
- * | [WeightedElementsComplicationData]   | WEIGHTED_ELEMENTS |
+ *   tag should be a comma separated list of types supported by the data source, from this table: |
+ *   Androidx class | Tag name | |--------------------------------------|-------------------| |
+ *   [GoalProgressComplicationData] | GOAL_PROGRESS | | [LongTextComplicationData] | LONG_TEXT | |
+ *   [MonochromaticImageComplicationData] | ICON | | [PhotoImageComplicationData] | LARGE_IMAGE | |
+ *   [RangedValueComplicationData] | RANGED_TEXT | | [ShortTextComplicationData] | SHORT_TEXT | |
+ *   [SmallImageComplicationData] | SMALL_IMAGE | | [WeightedElementsComplicationData] |
+ *   WEIGHTED_ELEMENTS |
  *
  * The order in which types are listed has no significance. In the case where a watch face supports
  * multiple types in a single complication slot, the watch face will determine which types it
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt
index 6188e69..13b3f52 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt
@@ -53,10 +53,7 @@
     public fun requestUpdate(vararg complicationInstanceIds: Int)
 
     public companion object {
-        /**
-         * The package of the service that accepts complication data source requests.
-         *
-         */
+        /** The package of the service that accepts complication data source requests. */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public const val UPDATE_REQUEST_RECEIVER_PACKAGE = "com.google.android.wearable.app"
 
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
index f16d472..720ff58 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
@@ -15,13 +15,13 @@
  */
 package androidx.wear.watchface.complications.datasource
 
-import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.content.Intent
 import android.content.res.Resources
 import android.os.Bundle
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.RemoteException
+import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.IComplicationManager
 import android.support.wearable.complications.IComplicationProvider
 import android.util.Log
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
index 2e5e3c8..8887171d 100644
--- 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
@@ -435,6 +435,7 @@
 
         companion object {
             private const val VERSION_NUMBER = 20
+
             internal fun putIfNotNull(fields: Bundle, field: String, value: Parcelable?) {
                 if (value != null) {
                     fields.putParcelable(field, value)
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
index 89fbc08..94db677 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
@@ -16,10 +16,10 @@
 
 package androidx.wear.watchface.complications.data
 
-import android.support.wearable.complications.ComplicationData as WireComplicationData
-import android.support.wearable.complications.ComplicationText as WireComplicationText
 import android.icu.util.ULocale
+import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.ComplicationData
+import android.support.wearable.complications.ComplicationText as WireComplicationText
 import androidx.annotation.MainThread
 import androidx.annotation.RestrictTo
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
index e34d53c..1215a97 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
@@ -907,6 +907,20 @@
         displayPolicy = displayPolicy,
         fallback = fallback,
     ) {
+
+    init {
+        require(min <= max) { "min must be lower than or equal to max" }
+        require(value == PLACEHOLDER || value in min..max) { "value must be between min and max" }
+        require(max != Float.MAX_VALUE) { "Float.MAX_VALUE is reserved and can't be used for max" }
+        require(monochromaticImage != null || smallImage != null || text != null || title != null) {
+            "At least one of monochromaticImage, smallImage, text or title must be set"
+        }
+        if (valueType == TYPE_PERCENTAGE) {
+            require(min == 0f)
+            require(max == 100f)
+        }
+    }
+
     /**
      * The [DynamicFloat] optionally set by the data source. If present the system will dynamically
      * evaluate this and store the result in [value]. Watch faces can typically ignore this field.
@@ -988,16 +1002,6 @@
 
         @RangedValueType private var valueType: Int = TYPE_UNDEFINED
 
-        init {
-            require(min <= max) { "min must be lower than or equal to max" }
-            require(value == PLACEHOLDER || value in min..max) {
-                "value must be between min and max"
-            }
-            require(max != Float.MAX_VALUE) {
-                "Float.MAX_VALUE is reserved and can't be used for max"
-            }
-        }
-
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
             this.tapAction = tapAction
@@ -1043,17 +1047,8 @@
         }
 
         /** Builds the [RangedValueComplicationData]. */
-        public override fun build(): RangedValueComplicationData {
-            require(
-                monochromaticImage != null || smallImage != null || text != null || title != null
-            ) {
-                "At least one of monochromaticImage, smallImage, text or title must be set"
-            }
-            if (valueType == TYPE_PERCENTAGE) {
-                require(min == 0f)
-                require(max == 100f)
-            }
-            return RangedValueComplicationData(
+        public override fun build() =
+            RangedValueComplicationData(
                 value,
                 valueExpression,
                 min,
@@ -1073,7 +1068,6 @@
                 displayPolicy,
                 fallback,
             )
-        }
     }
 
     override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
@@ -1273,6 +1267,16 @@
         displayPolicy = displayPolicy,
         fallback = fallback,
     ) {
+
+    init {
+        require(targetValue != Float.MAX_VALUE) {
+            "Float.MAX_VALUE is reserved and can't be used for target"
+        }
+        require(monochromaticImage != null || smallImage != null || text != null || title != null) {
+            "At least one of monochromaticImage, smallImage, text or title must be set"
+        }
+    }
+
     /**
      * The [DynamicFloat] optionally set by the data source. If present the system will dynamically
      * evaluate this and store the result in [value]. Watch faces can typically ignore this field.
@@ -1345,12 +1349,6 @@
         private var text: ComplicationText? = null
         private var colorRamp: ColorRamp? = null
 
-        init {
-            require(targetValue != Float.MAX_VALUE) {
-                "Float.MAX_VALUE is reserved and can't be used for target"
-            }
-        }
-
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
             this.tapAction = tapAction
@@ -1387,13 +1385,8 @@
         }
 
         /** Builds the [GoalProgressComplicationData]. */
-        public override fun build(): GoalProgressComplicationData {
-            require(
-                monochromaticImage != null || smallImage != null || text != null || title != null
-            ) {
-                "At least one of monochromaticImage, smallImage, text or title must be set"
-            }
-            return GoalProgressComplicationData(
+        public override fun build() =
+            GoalProgressComplicationData(
                 value,
                 valueExpression,
                 targetValue,
@@ -1411,7 +1404,6 @@
                 displayPolicy,
                 fallback = fallback,
             )
-        }
     }
 
     override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
@@ -1585,6 +1577,12 @@
         displayPolicy = displayPolicy,
         fallback = fallback,
     ) {
+
+    init {
+        require(monochromaticImage != null || smallImage != null || text != null || title != null) {
+            "At least one of monochromaticImage, smallImage, text or title must be set"
+        }
+    }
     /**
      * Describes a single value within a [WeightedElementsComplicationData].
      *
@@ -1710,13 +1708,8 @@
         public fun setText(text: ComplicationText?): Builder = apply { this.text = text }
 
         /** Builds the [GoalProgressComplicationData]. */
-        public override fun build(): WeightedElementsComplicationData {
-            require(
-                monochromaticImage != null || smallImage != null || text != null || title != null
-            ) {
-                "At least one of monochromaticImage, smallImage, text or title must be set"
-            }
-            return WeightedElementsComplicationData(
+        public override fun build() =
+            WeightedElementsComplicationData(
                 elements,
                 elementBackgroundColor,
                 monochromaticImage,
@@ -1732,7 +1725,6 @@
                 displayPolicy,
                 fallback,
             )
-        }
     }
 
     override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
index de0c253..9d2d48f 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
@@ -516,6 +516,7 @@
     override fun isPlaceholder(): Boolean = delegate.isPlaceholder()
 
     override fun isAlwaysEmpty() = delegate.isAlwaysEmpty
+
     override fun getTimeDependentText(): TimeDependentText = delegate.timeDependentText
 
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) override fun toWireComplicationText() = delegate
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
index 2ed2aba..86183f0 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
@@ -48,7 +48,6 @@
      * Converts this value to the integer value used for serialization.
      *
      * This is only needed internally to convert to the underlying communication protocol.
-     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY) public fun toWireComplicationType(): Int = wireType
 
@@ -59,7 +58,6 @@
          * Converts the integer value used for serialization into a [ComplicationType].
          *
          * This is only needed internally to convert to the underlying communication protocol.
-         *
          */
         @OptIn(ComplicationExperimental::class)
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -89,7 +87,6 @@
          * This is only needed internally to convert to the underlying communication protocol.
          *
          * Needed to access this conveniently in Java.
-         *
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
@@ -102,7 +99,6 @@
          * This is only needed internally to convert to the underlying communication protocol.
          *
          * Needed to access this conveniently in Java.
-         *
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
@@ -112,7 +108,6 @@
         /**
          * Converts an array of integer values used for serialization into the corresponding list of
          * [ComplicationType].
-         *
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
@@ -126,7 +121,6 @@
  * types.
  *
  * This is only needed internally to convert to the underlying communication protocol.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public fun Collection<ComplicationType>.toWireTypes(): IntArray =
@@ -137,7 +131,6 @@
  * [ComplicationType] to .
  *
  * This is only needed internally to convert to the underlying communication protocol.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public fun IntArray.toApiComplicationTypes(): Array<ComplicationType> =
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt
index b55837b..88d4d32 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt
@@ -24,10 +24,7 @@
 import androidx.annotation.RestrictTo
 import java.util.Objects
 
-/**
- * Returns true if the [Icon]s are equal.
- *
- */
+/** Returns true if the [Icon]s are equal. */
 infix fun Icon?.iconEquals(other: Icon?): Boolean =
     this === other ||
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -36,10 +33,7 @@
             this == other
         }
 
-/**
- * Creates a hash code for the [Icon].
- *
- */
+/** Creates a hash code for the [Icon]. */
 fun Icon.iconHashCode(): Int =
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
         IconP.hashCode(this)
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt
index 7239b5e..18e1f0f 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt
@@ -29,7 +29,6 @@
 /**
  * Wrapper around [Trace.beginSection] and [Trace.endSection] which helps reduce boilerplate by
  * taking advantage of RAII like [Closeable] in a try block.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class TraceEvent(traceName: String) : Closeable {
@@ -45,7 +44,6 @@
 /**
  * Wrapper around [Trace.beginAsyncSection] which helps reduce boilerplate by taking advantage of
  * RAII like [Trace.endAsyncSection] in a try block, and by dealing with API version support.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class AsyncTraceEvent(private val traceName: String) : Closeable {
@@ -86,10 +84,7 @@
     }
 }
 
-/**
- * Wrapper around [CoroutineScope.launch] with an async trace event.
- *
- */
+/** Wrapper around [CoroutineScope.launch] with an async trace event. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public fun CoroutineScope.launchWithTracing(
     traceEventName: String,
diff --git a/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt b/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt
index b771fdb..95542cf 100644
--- a/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt
+++ b/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt
@@ -27,10 +27,7 @@
 
 /** Defines attributes to customize appearance of rendered [ ]. */
 public class ComplicationStyle {
-    /**
-     * Constants used to define border styles for complicationSlots.
-     *
-     */
+    /** Constants used to define border styles for complicationSlots. */
     @Retention(AnnotationRetention.SOURCE)
     @IntDef(BORDER_STYLE_NONE, BORDER_STYLE_SOLID, BORDER_STYLE_DASHED)
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -194,6 +191,7 @@
                 }
             isDirty = true
         }
+
     /** The dash width to be used when drawing borders of type [.BORDER_STYLE_DASHED]. */
     public var borderDashWidth: Int
         @Px get() = mBorderDashWidth
@@ -209,6 +207,7 @@
             mBorderDashGap = borderDashGap
             isDirty = true
         }
+
     /**
      * The border radius to be applied to the corners of the bounds of the complication in active
      * mode. Border radius will be limited to the half of width or height, depending on which one is
@@ -282,10 +281,7 @@
         isDirty = true
     }
 
-    /**
-     * Returns a copy of the ComplicationStyle [tint]ed by [tintColor].
-     *
-     */
+    /** Returns a copy of the ComplicationStyle [tint]ed by [tintColor]. */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     fun asTinted(tintColor: Int): ComplicationStyle =
         ComplicationStyle(this).apply {
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt
index 873ef14..af4e203 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt
@@ -397,10 +397,7 @@
         return result
     }
 
-    /**
-     * Converts this value to [WireComplicationProviderInfo] object used for serialization.
-     *
-     */
+    /** Converts this value to [WireComplicationProviderInfo] object used for serialization. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun toWireComplicationProviderInfo(): WireComplicationProviderInfo =
         WireComplicationProviderInfo(
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt
index ec9b539..d3770b2 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt
@@ -129,7 +129,6 @@
          * RectF>, backfilling with empty [RectF]s. This method is necessary because there can be a
          * skew between the version of the library between the watch face and the system which would
          * otherwise be problematic if new complication types have been introduced.
-         *
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         fun createFromPartialMap(
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
index a50594f..b3d620f 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
@@ -179,10 +179,7 @@
         public const val DATA_SOURCE_DAY_AND_DATE: Int = 16
     }
 
-    /**
-     * System complication data source id as defined in [SystemDataSources].
-     *
-     */
+    /** System complication data source id as defined in [SystemDataSources]. */
     @IntDef(
         NO_DATA_SOURCE,
         DATA_SOURCE_WATCH_BATTERY,
diff --git a/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt b/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt
index 4108ebf..f40e087 100644
--- a/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt
+++ b/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt
@@ -33,11 +33,11 @@
 import androidx.wear.watchface.complications.data.SmallImageComplicationData
 import com.google.common.truth.Truth.assertThat
 import kotlin.jvm.java
-import org.mockito.Mockito
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito
 
 @RunWith(SharedRobolectricTestRunner::class)
 public class ComplicationDataSourceInfoRetrieverTest {
@@ -58,16 +58,16 @@
 
             val testData: ComplicationData =
                 LongTextComplicationData.Builder(
-                    PlainComplicationText.Builder("Test Text").build(),
-                    ComplicationText.Companion.EMPTY
-                )
+                        PlainComplicationText.Builder("Test Text").build(),
+                        ComplicationText.Companion.EMPTY
+                    )
                     .build()
 
             Mockito.doAnswer {
-                val callback = it.arguments[2] as IPreviewComplicationDataCallback
-                callback.updateComplicationData(testData.asWireComplicationData())
-                true
-            }
+                    val callback = it.arguments[2] as IPreviewComplicationDataCallback
+                    callback.updateComplicationData(testData.asWireComplicationData())
+                    true
+                }
                 .`when`(mockService)
                 .requestPreviewComplicationData(
                     eq(component),
@@ -82,13 +82,13 @@
                 )!!
             assertThat(previewData.type).isEqualTo(type)
             assertThat(
-                (previewData as LongTextComplicationData)
-                    .text
-                    .getTextAt(
-                        ApplicationProvider.getApplicationContext<Context>().resources,
-                        java.time.Instant.EPOCH
-                    )
-            )
+                    (previewData as LongTextComplicationData)
+                        .text
+                        .getTextAt(
+                            ApplicationProvider.getApplicationContext<Context>().resources,
+                            java.time.Instant.EPOCH
+                        )
+                )
                 .isEqualTo("Test Text")
         }
     }
@@ -103,10 +103,10 @@
             Mockito.`when`(mockService.asBinder()).thenReturn(mockBinder)
 
             Mockito.doAnswer {
-                val callback = it.arguments[2] as IPreviewComplicationDataCallback
-                callback.updateComplicationData(null)
-                true
-            }
+                    val callback = it.arguments[2] as IPreviewComplicationDataCallback
+                    callback.updateComplicationData(null)
+                    true
+                }
                 .`when`(mockService)
                 .requestPreviewComplicationData(
                     eq(component),
@@ -115,11 +115,11 @@
                 )
 
             assertThat(
-                complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
-                    component,
-                    type
+                    complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
+                        component,
+                        type
+                    )
                 )
-            )
                 .isNull()
         }
     }
@@ -134,11 +134,11 @@
             Mockito.`when`(mockService.asBinder()).thenReturn(mockBinder)
 
             assertThat(
-                complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
-                    component,
-                    type
+                    complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
+                        component,
+                        type
+                    )
                 )
-            )
                 .isNull()
         }
     }
@@ -160,11 +160,11 @@
                 )
 
             assertThat(
-                complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
-                    component,
-                    type
+                    complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
+                        component,
+                        type
+                    )
                 )
-            )
                 .isNull()
         }
     }
@@ -192,21 +192,21 @@
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val shortTextPreviewData =
             ComplicationDataSourceInfo(
-                "applicationName",
-                "complicationName",
-                icon,
-                ComplicationType.SHORT_TEXT,
-                componentName = null
-            )
+                    "applicationName",
+                    "complicationName",
+                    icon,
+                    ComplicationType.SHORT_TEXT,
+                    componentName = null
+                )
                 .fallbackPreviewData as ShortTextComplicationData
         assertThat(shortTextPreviewData.text.getTextAt(resources, java.time.Instant.EPOCH))
             .isEqualTo("complic")
         assertThat(
-            shortTextPreviewData.contentDescription!!.getTextAt(
-                resources,
-                java.time.Instant.EPOCH
+                shortTextPreviewData.contentDescription!!.getTextAt(
+                    resources,
+                    java.time.Instant.EPOCH
+                )
             )
-        )
             .isEqualTo("complicationName")
         assertThat(shortTextPreviewData.monochromaticImage!!.image).isEqualTo(icon)
     }
@@ -216,21 +216,21 @@
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val longTextPreviewData =
             ComplicationDataSourceInfo(
-                "applicationName",
-                "complicationName",
-                icon,
-                ComplicationType.LONG_TEXT,
-                componentName = null
-            )
+                    "applicationName",
+                    "complicationName",
+                    icon,
+                    ComplicationType.LONG_TEXT,
+                    componentName = null
+                )
                 .fallbackPreviewData as LongTextComplicationData
         assertThat(longTextPreviewData.text.getTextAt(resources, java.time.Instant.EPOCH))
             .isEqualTo("complicationName")
         assertThat(
-            longTextPreviewData.contentDescription!!.getTextAt(
-                resources,
-                java.time.Instant.EPOCH
+                longTextPreviewData.contentDescription!!.getTextAt(
+                    resources,
+                    java.time.Instant.EPOCH
+                )
             )
-        )
             .isEqualTo("complicationName")
         assertThat(longTextPreviewData.monochromaticImage!!.image).isEqualTo(icon)
     }
@@ -240,20 +240,20 @@
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val smallImagePreviewData =
             ComplicationDataSourceInfo(
-                "applicationName",
-                "complicationName",
-                icon,
-                ComplicationType.SMALL_IMAGE,
-                componentName = null
-            )
+                    "applicationName",
+                    "complicationName",
+                    icon,
+                    ComplicationType.SMALL_IMAGE,
+                    componentName = null
+                )
                 .fallbackPreviewData as SmallImageComplicationData
         assertThat(smallImagePreviewData.smallImage.image).isEqualTo(icon)
         assertThat(
-            smallImagePreviewData.contentDescription!!.getTextAt(
-                resources,
-                java.time.Instant.EPOCH
+                smallImagePreviewData.contentDescription!!.getTextAt(
+                    resources,
+                    java.time.Instant.EPOCH
+                )
             )
-        )
             .isEqualTo("complicationName")
     }
 
@@ -262,20 +262,20 @@
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val photoImagePreviewData =
             ComplicationDataSourceInfo(
-                "applicationName",
-                "complicationName",
-                icon,
-                ComplicationType.PHOTO_IMAGE,
-                componentName = null
-            )
+                    "applicationName",
+                    "complicationName",
+                    icon,
+                    ComplicationType.PHOTO_IMAGE,
+                    componentName = null
+                )
                 .fallbackPreviewData as PhotoImageComplicationData
         assertThat(photoImagePreviewData.photoImage).isEqualTo(icon)
         assertThat(
-            photoImagePreviewData.contentDescription!!.getTextAt(
-                resources,
-                java.time.Instant.EPOCH
+                photoImagePreviewData.contentDescription!!.getTextAt(
+                    resources,
+                    java.time.Instant.EPOCH
+                )
             )
-        )
             .isEqualTo("complicationName")
     }
 
@@ -284,20 +284,20 @@
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val monochromaticImagePreviewData =
             ComplicationDataSourceInfo(
-                "applicationName",
-                "complicationName",
-                icon,
-                ComplicationType.MONOCHROMATIC_IMAGE,
-                componentName = null
-            )
+                    "applicationName",
+                    "complicationName",
+                    icon,
+                    ComplicationType.MONOCHROMATIC_IMAGE,
+                    componentName = null
+                )
                 .fallbackPreviewData as MonochromaticImageComplicationData
         assertThat(monochromaticImagePreviewData.monochromaticImage.image).isEqualTo(icon)
         assertThat(
-            monochromaticImagePreviewData.contentDescription!!.getTextAt(
-                resources,
-                java.time.Instant.EPOCH
+                monochromaticImagePreviewData.contentDescription!!.getTextAt(
+                    resources,
+                    java.time.Instant.EPOCH
+                )
             )
-        )
             .isEqualTo("complicationName")
     }
 
@@ -306,12 +306,12 @@
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val rangedValuePreviewData =
             ComplicationDataSourceInfo(
-                "applicationName",
-                "complicationName",
-                icon,
-                ComplicationType.RANGED_VALUE,
-                componentName = null
-            )
+                    "applicationName",
+                    "complicationName",
+                    icon,
+                    ComplicationType.RANGED_VALUE,
+                    componentName = null
+                )
                 .fallbackPreviewData as RangedValueComplicationData
         assertThat(rangedValuePreviewData.min).isEqualTo(0.0f)
         assertThat(rangedValuePreviewData.max).isEqualTo(100.0f)
@@ -320,11 +320,11 @@
             .isEqualTo("complicationName")
         assertThat(rangedValuePreviewData.monochromaticImage!!.image).isEqualTo(icon)
         assertThat(
-            rangedValuePreviewData.contentDescription!!.getTextAt(
-                resources,
-                java.time.Instant.EPOCH
+                rangedValuePreviewData.contentDescription!!.getTextAt(
+                    resources,
+                    java.time.Instant.EPOCH
+                )
             )
-        )
             .isEqualTo("complicationName")
     }
 
@@ -332,27 +332,30 @@
     public fun complicationDataSourceInfo_equals() {
         val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
         val icon2 = android.graphics.drawable.Icon.createWithContentUri("icon")
-        val a = ComplicationDataSourceInfo(
+        val a =
+            ComplicationDataSourceInfo(
                 "applicationName",
                 "complicationName",
                 icon,
                 ComplicationType.RANGED_VALUE,
                 componentName = null
             )
-        val b = ComplicationDataSourceInfo(
+        val b =
+            ComplicationDataSourceInfo(
                 "applicationName",
                 "complicationName",
                 icon2,
                 ComplicationType.RANGED_VALUE,
                 componentName = null
             )
-        val c = ComplicationDataSourceInfo(
-            "applicationName2",
-            "complicationName2",
-            icon,
-            ComplicationType.RANGED_VALUE,
-            componentName = null
-        )
+        val c =
+            ComplicationDataSourceInfo(
+                "applicationName2",
+                "complicationName2",
+                icon,
+                ComplicationType.RANGED_VALUE,
+                componentName = null
+            )
 
         // Test two identical ComplicationDataSourceInfo with different references.
         assertThat(a).isEqualTo(b)
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt
index d4422b0..217353a 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt
@@ -19,10 +19,7 @@
 import android.app.WallpaperManager
 import androidx.annotation.RestrictTo
 
-/**
- * Shared constants between client and implementation.
- *
- */
+/** Shared constants between client and implementation. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class Constants {
     // Not instantiable.
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt
index db2f239..16c2957 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt
@@ -21,10 +21,7 @@
 import android.os.Parcelable
 import androidx.annotation.RestrictTo
 
-/**
- * Wraps a Parcelable.
- *
- */
+/** Wraps a Parcelable. */
 @SuppressLint("BanParcelableUsage")
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class ParcelableWrapper(val parcelable: Parcelable) : Parcelable {
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt
index ad83ad4..4252e0b 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt
@@ -27,7 +27,6 @@
 /**
  * This class requires API level 27 and is only intended for use in conjunction with
  * wear-watchface-client which also requires API level 27.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class SharedMemoryImage {
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt
index ce40b16..a29c0ce 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt
@@ -32,7 +32,6 @@
  * of your [WatchFaceService.Engine.onCreate] override.
  *
  * <p>To construct a WatchFaceStyle use [WatchFaceStyle.Builder].
- *
  */
 @SuppressWarnings("BanParcelableUsage")
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
diff --git a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt
index 8f354ad..cebcf7b 100644
--- a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt
+++ b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt
@@ -59,10 +59,7 @@
     }
 }
 
-/**
- * Configuration view for watch faces with multiple complicationSlots.
- *
- */
+/** Configuration view for watch faces with multiple complicationSlots. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @SuppressWarnings(
     "ViewConstructor", // Internal view, not intended for use by tools.
@@ -82,8 +79,8 @@
             // TODO(alexclarke): This button is a Rect which makes the tap animation look bad.
             if (
                 entry.value.fixedComplicationDataSource ||
-                !entry.value.isEnabled ||
-                entry.key == watchFaceConfigActivity.editorSession.backgroundComplicationSlotId
+                    !entry.value.isEnabled ||
+                    entry.key == watchFaceConfigActivity.editorSession.backgroundComplicationSlotId
             ) {
                 // Do not create a button for fixed complicationSlots, disabled complicationSlots,
                 // or background complicationSlots.
diff --git a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt
index 9d0825c..e66ab3b 100644
--- a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt
+++ b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt
@@ -40,9 +40,9 @@
 import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
 import androidx.wear.watchface.style.UserStyleSetting.CustomValueUserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting.DoubleRangeOption
+import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting.LongRangeOption
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index 32cdbdc..f99c2d7 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -600,24 +600,24 @@
         val mockSurfaceHolder = `mock`(SurfaceHolder::class.java)
         `when`(mockSurfaceHolder.surfaceFrame).thenReturn(screenBounds)
         @Suppress("Deprecation")
-        val fakeRenderer = object : Renderer.CanvasRenderer(
-            mockSurfaceHolder,
-            userStyleRepository,
-            MutableWatchState().asWatchState(),
-            CanvasType.HARDWARE,
-            interactiveDrawModeUpdateDelayMillis = 16,
-            clearWithBackgroundTintBeforeRenderingHighlightLayer = false
-        ) {
-            override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
-            }
+        val fakeRenderer =
+            object :
+                Renderer.CanvasRenderer(
+                    mockSurfaceHolder,
+                    userStyleRepository,
+                    MutableWatchState().asWatchState(),
+                    CanvasType.HARDWARE,
+                    interactiveDrawModeUpdateDelayMillis = 16,
+                    clearWithBackgroundTintBeforeRenderingHighlightLayer = false
+                ) {
+                override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
 
-            override fun renderHighlightLayer(
-                canvas: Canvas,
-                bounds: Rect,
-                zonedDateTime: ZonedDateTime
-            ) {
+                override fun renderHighlightLayer(
+                    canvas: Canvas,
+                    bounds: Rect,
+                    zonedDateTime: ZonedDateTime
+                ) {}
             }
-        }
 
         val complicationSlotsManager =
             ComplicationSlotsManager(complicationSlots, userStyleRepository, fakeRenderer)
@@ -1835,7 +1835,6 @@
         EditorService.globalEditorService.unregisterObserver(observerId)
     }
 
-    @SdkSuppress(maxSdkVersion = 32) // b/275361339
     @Test
     @Suppress("Deprecation") // userStyleSettings
     public fun commit_headless() {
@@ -1887,7 +1886,6 @@
         EditorService.globalEditorService.unregisterObserver(observerId)
     }
 
-    @SdkSuppress(maxSdkVersion = 32) // b/275361339
     @SuppressLint("NewApi")
     @Suppress("Deprecation") // userStyleSettings
     @Test
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
index 6fd3d02..1eacf82 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
@@ -716,10 +716,7 @@
      */
     public val userStyle: StateFlow<UserStyle> by CurrentUserStyleRepository::mutableUserStyle
 
-    /**
-     * The UserStyle options must be from the supplied [UserStyleSchema].
-     *
-     */
+    /** The UserStyle options must be from the supplied [UserStyleSchema]. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun updateUserStyle(newUserStyle: UserStyle) {
         validateUserStyle(newUserStyle)
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index 8ce3c6c..a79956a 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -242,7 +242,6 @@
      * used.
      *
      * Note this method can be slow.
-     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun estimateWireSizeInBytesAndValidateIconDimensions(
@@ -1028,6 +1027,7 @@
                 nameResourceId?.let { dos.writeInt(it) }
                 screenReaderNameResourceId?.let { dos.writeInt(it) }
             }
+
             /**
              * Constructs a [ComplicationSlotOverlay].Builder.
              *
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
index 1a449b9..c98331c 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
@@ -26,8 +26,8 @@
 import androidx.wear.watchface.style.UserStyleSetting.BooleanUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.CustomValueUserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting.ListOption
 import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting
@@ -194,10 +194,11 @@
                 listOf(WatchFaceLayer.BASE),
                 true
             )
-        val styleSetting4 = LargeCustomValueUserStyleSetting(
-            listOf(WatchFaceLayer.BASE),
-            "default".encodeToByteArray()
-        )
+        val styleSetting4 =
+            LargeCustomValueUserStyleSetting(
+                listOf(WatchFaceLayer.BASE),
+                "default".encodeToByteArray()
+            )
         val srcSchema =
             UserStyleSchema(listOf(styleSetting1, styleSetting2, styleSetting3, styleSetting4))
 
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt
index 6550522..dcc2146 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt
@@ -29,14 +29,14 @@
 import androidx.wear.watchface.complications.data.ComplicationType.MONOCHROMATIC_IMAGE
 import androidx.wear.watchface.complications.data.ComplicationType.SHORT_TEXT
 import com.google.common.truth.Truth.assertThat
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.verify
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
 
 const val TIME_OUT_MILLIS = 500L
 
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
index eafefe5..8c7e17e 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
-import androidx.test.filters.SdkSuppress
 import androidx.wear.watchface.control.InteractiveInstanceManager
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.UserStyleSetting
@@ -39,7 +38,6 @@
         InteractiveInstanceManager.setParameterlessEngine(null)
     }
 
-    @SdkSuppress(maxSdkVersion = 32) // b/275361339
     @Test
     fun measuresWatchFaceIconsFromCustomContext() {
         val context: Context = ApplicationProvider.getApplicationContext()
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
index 53f13686..7bb5522 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
@@ -543,12 +543,15 @@
 
     @Test
     public fun createWatchFaceService_throwsOnInvalidClass() {
-        assertThat(WatchFaceControlService()
-            .createWatchFaceService(
-                ComponentName(
-                    ApplicationProvider.getApplicationContext(),
-                    WatchFaceControlServiceTest::class.java
-                )
-            )).isNull()
+        assertThat(
+                WatchFaceControlService()
+                    .createWatchFaceService(
+                        ComponentName(
+                            ApplicationProvider.getApplicationContext(),
+                            WatchFaceControlServiceTest::class.java
+                        )
+                    )
+            )
+            .isNull()
     }
 }
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
index 644a68a..a7130b9 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
@@ -28,7 +28,6 @@
 /**
  * This class decouples [BroadcastEventObserver]s from the actual broadcast event receivers to make
  * testing easier.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class BroadcastsReceiver
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index 0def254..41de246 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -633,8 +633,7 @@
             supportedTypes: List<ComplicationType>,
             defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
             bounds: ComplicationSlotBounds,
-            @Suppress("HiddenTypeParameter")
-            boundingArc: BoundingArc,
+            @Suppress("HiddenTypeParameter") boundingArc: BoundingArc,
             complicationTapFilter: ComplicationTapFilter =
                 object : ComplicationTapFilter {
                     override fun hitTest(
@@ -808,25 +807,28 @@
 
         /** Constructs the [ComplicationSlot]. */
         public fun build(): ComplicationSlot {
-            require(defaultDataSourcePolicy.primaryDataSourceDefaultType == null ||
-                defaultDataSourcePolicy.primaryDataSourceDefaultType in supportedTypes
+            require(
+                defaultDataSourcePolicy.primaryDataSourceDefaultType == null ||
+                    defaultDataSourcePolicy.primaryDataSourceDefaultType in supportedTypes
             ) {
                 "defaultDataSourcePolicy.primaryDataSourceDefaultType " +
                     "${defaultDataSourcePolicy.primaryDataSourceDefaultType} must be in the" +
                     " supportedTypes list: $supportedTypes"
             }
 
-            require(defaultDataSourcePolicy.secondaryDataSourceDefaultType == null ||
-                defaultDataSourcePolicy.secondaryDataSourceDefaultType in supportedTypes
+            require(
+                defaultDataSourcePolicy.secondaryDataSourceDefaultType == null ||
+                    defaultDataSourcePolicy.secondaryDataSourceDefaultType in supportedTypes
             ) {
                 "defaultDataSourcePolicy.secondaryDataSourceDefaultType " +
                     "${defaultDataSourcePolicy.secondaryDataSourceDefaultType} must be in the" +
                     " supportedTypes list: $supportedTypes"
             }
 
-            require(defaultDataSourcePolicy.systemDataSourceFallbackDefaultType ==
-                ComplicationType.NOT_CONFIGURED ||
-                defaultDataSourcePolicy.systemDataSourceFallbackDefaultType in supportedTypes
+            require(
+                defaultDataSourcePolicy.systemDataSourceFallbackDefaultType ==
+                    ComplicationType.NOT_CONFIGURED ||
+                    defaultDataSourcePolicy.systemDataSourceFallbackDefaultType in supportedTypes
             ) {
                 "defaultDataSourcePolicy.systemDataSourceFallbackDefaultType " +
                     "${defaultDataSourcePolicy.systemDataSourceFallbackDefaultType} must be in " +
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index f996759..28a76f1 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -175,7 +175,7 @@
 ) {
     /** The [SurfaceHolder] that [renderInternal] will draw into. */
     public var surfaceHolder: SurfaceHolder = surfaceHolder
-       protected set
+        protected set
 
     @OptIn(WatchFaceExperimental::class) private var pendingWatchFaceColors: WatchFaceColors? = null
     private var pendingWatchFaceColorsSet = false
@@ -379,7 +379,7 @@
      * @param zonedDateTime The [ZonedDateTime] to use when rendering the watch face
      * @param renderParameters The [RenderParameters] to use when rendering the watch face
      * @param screenShotSurfaceHolder The [SurfaceHolder] containing the [Surface] to render into.
-     * This is assumed to have the same dimensions as the screen.
+     *   This is assumed to have the same dimensions as the screen.
      */
     @Suppress("HiddenAbstractMethod")
     @UiThread
@@ -1448,13 +1448,14 @@
 
             runBlocking {
                 glContextLock.withLock {
-                    val tempEglSurface = EGL14.eglCreateWindowSurface(
-                        eglDisplay,
-                        eglConfig,
-                        surfaceHolder.surface,
-                        eglSurfaceAttribList,
-                        0
-                    )
+                    val tempEglSurface =
+                        EGL14.eglCreateWindowSurface(
+                            eglDisplay,
+                            eglConfig,
+                            surfaceHolder.surface,
+                            eglSurfaceAttribList,
+                            0
+                        )
 
                     if (
                         !EGL14.eglMakeCurrent(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index c0cb13a..edb9c49 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -84,7 +84,6 @@
 /**
  * The type of watch face, whether it's digital or analog. This influences the time displayed for
  * remote previews.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @IntDef(value = [WatchFaceType.DIGITAL, WatchFaceType.ANALOG])
@@ -158,10 +157,7 @@
             componentNameToEditorDelegate.clear()
         }
 
-        /**
-         * For use by on watch face editors.
-         *
-         */
+        /** For use by on watch face editors. */
         @JvmStatic
         @UiThread
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -180,9 +176,7 @@
         }
 
         @UiThread
-        internal fun createWatchFaceServiceOld(
-            componentName: ComponentName
-        ): WatchFaceService {
+        internal fun createWatchFaceServiceOld(componentName: ComponentName): WatchFaceService {
             // Attempt to construct the class for the specified watchFaceName, failing if it either
             // doesn't exist or isn't a [WatchFaceService].
             val watchFaceServiceClass =
@@ -205,10 +199,13 @@
             context: Context
         ): WatchFaceService {
             // Resolve the WatchFaceControlService and construct WatchFaceService using its API
-            val services = context.packageManager.queryIntentServices(Intent(
-                WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
-                setPackage(context.packageName)
-            }, 0)
+            val services =
+                context.packageManager.queryIntentServices(
+                    Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+                        setPackage(context.packageName)
+                    },
+                    0
+                )
 
             if (services.size != 1)
                 throw IllegalArgumentException(
@@ -218,9 +215,7 @@
 
             val watchFaceControlServiceClass =
                 Class.forName(services[0].serviceInfo.name)
-                    ?: throw IllegalArgumentException(
-                        "Can't find ${services[0].serviceInfo.name}"
-                    )
+                    ?: throw IllegalArgumentException("Can't find ${services[0].serviceInfo.name}")
 
             val watchFaceControlService =
                 watchFaceControlServiceClass.getConstructor().newInstance()
@@ -230,10 +225,7 @@
                 ?: throw IllegalArgumentException("Can't create ${componentName.className}")
         }
 
-        /**
-         * For use by on watch face editors.
-         *
-         */
+        /** For use by on watch face editors. */
         @SuppressLint("NewApi")
         @JvmStatic
         @UiThread
@@ -243,25 +235,21 @@
             params: HeadlessWatchFaceInstanceParams,
             context: Context
         ): EditorDelegate {
-            val watchFaceService = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-                createWatchFaceService(componentName, context)
-            } else {
-                createWatchFaceServiceOld(componentName)
-            }.apply {
-                setContext(context)
-            }
+            val watchFaceService =
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                        createWatchFaceService(componentName, context)
+                    } else {
+                        createWatchFaceServiceOld(componentName)
+                    }
+                    .apply { setContext(context) }
 
-            val engine =
-                watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            val engine = watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
             val headlessWatchFaceImpl = engine.createHeadlessInstance(params)
             return engine.deferredWatchFaceImpl.await().WFEditorDelegate(headlessWatchFaceImpl)
         }
     }
 
-    /**
-     * Delegate used by on watch face editors.
-     *
-     */
+    /** Delegate used by on watch face editors. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public interface EditorDelegate {
         /** The [WatchFace]'s [UserStyleSchema]. */
@@ -306,10 +294,7 @@
         )
     }
 
-    /**
-     * Used to inform EditorSession about changes to [ComplicationSlot.configExtras].
-     *
-     */
+    /** Used to inform EditorSession about changes to [ComplicationSlot.configExtras]. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public interface ComplicationSlotConfigExtrasChangeCallback {
         public fun onComplicationSlotConfigExtrasChanged()
@@ -557,8 +542,7 @@
     private val watchFaceHostApi: WatchFaceHostApi,
     private val watchState: WatchState,
     internal val currentUserStyleRepository: CurrentUserStyleRepository,
-    @get:VisibleForTesting
-    public var complicationSlotsManager: ComplicationSlotsManager,
+    @get:VisibleForTesting public var complicationSlotsManager: ComplicationSlotsManager,
     internal val broadcastsObserver: BroadcastsObserver,
     internal var broadcastsReceiver: BroadcastsReceiver?
 ) {
@@ -1176,19 +1160,20 @@
         hostToken: IBinder,
         width: Int,
         height: Int
-    ): RemoteWatchFaceView? = TraceEvent("WatchFaceImpl.createRemoteWatchFaceView").use {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            return CreateRemoteWatchFaceViewHelper.createRemoteWatchFaceView(
-                watchFaceHostApi,
-                this,
-                hostToken,
-                width,
-                height
-            )
-        } else {
-            return null
+    ): RemoteWatchFaceView? =
+        TraceEvent("WatchFaceImpl.createRemoteWatchFaceView").use {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                return CreateRemoteWatchFaceViewHelper.createRemoteWatchFaceView(
+                    watchFaceHostApi,
+                    this,
+                    hostToken,
+                    width,
+                    height
+                )
+            } else {
+                return null
+            }
         }
-    }
 
     @UiThread
     @RequiresApi(27)
@@ -1286,27 +1271,26 @@
         height: Int
     ): RemoteWatchFaceView {
         val context = watchFaceHostApi.getContext()
-        val host = SurfaceControlViewHost(
-            context,
-            context.getSystemService(WindowManager::class.java).defaultDisplay,
-            hostToken
-        )
+        val host =
+            SurfaceControlViewHost(
+                context,
+                context.getSystemService(WindowManager::class.java).defaultDisplay,
+                hostToken
+            )
         val view = SurfaceView(context)
-        view.layoutParams = WindowManager.LayoutParams(
-            WindowManager.LayoutParams.WRAP_CONTENT,
-            WindowManager.LayoutParams.WRAP_CONTENT,
-            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
-            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
-            PixelFormat.TRANSLUCENT
-        ).apply {
-            title = "RemoteWatchFaceView"
-        }
+        view.layoutParams =
+            WindowManager.LayoutParams(
+                    WindowManager.LayoutParams.WRAP_CONTENT,
+                    WindowManager.LayoutParams.WRAP_CONTENT,
+                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+                    PixelFormat.TRANSLUCENT
+                )
+                .apply { title = "RemoteWatchFaceView" }
         host.setView(view, width, height)
-        return RemoteWatchFaceView(
-            view,
-            host,
-            watchFaceHostApi.getUiThreadCoroutineScope()
-        ) { surfaceHolder, params ->
+        return RemoteWatchFaceView(view, host, watchFaceHostApi.getUiThreadCoroutineScope()) {
+            surfaceHolder,
+            params ->
             val oldStyle = watchFaceImpl.currentUserStyleRepository.userStyle.value
             val instant = Instant.ofEpochMilli(params.calendarTimeMillis)
 
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
index fce93a2..3f160d0 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
@@ -28,10 +28,7 @@
 import java.time.Duration
 import kotlinx.coroutines.CoroutineScope
 
-/**
- * The API [WatchFaceImpl] uses to communicate with the system.
- *
- */
+/** The API [WatchFaceImpl] uses to communicate with the system. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public interface WatchFaceHostApi {
     /** The [WatchFaceService.SystemTimeProvider]. */
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 4cf18b94..0f9a4ba 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -429,17 +429,13 @@
             }
     }
 
-    /**
-     * The context used to resolve resources. Unlocks future work.
-     *
-     */
+    /** The context used to resolve resources. Unlocks future work. */
     protected open val resourcesContext: Context
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) get() = this
 
     /**
      * Returns the id of the XmlSchemaAndComplicationSlotsDefinition XML resource or 0 if it can't
      * be found.
-     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @Suppress("DEPRECATION")
@@ -618,7 +614,6 @@
     /**
      * Override to force the watchface to be regarded as being visible. This must not be used in
      * production code or significant battery life regressions may occur.
-     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) open fun forceIsVisibleForTesting() = false
 
@@ -653,10 +648,7 @@
 
     internal var backgroundThread: HandlerThread? = null
 
-    /**
-     * Interface for getting the current system time.
-     *
-     */
+    /** Interface for getting the current system time. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public interface SystemTimeProvider {
         /** Returns the current system time in milliseconds. */
@@ -719,6 +711,7 @@
     /** [Choreographer] isn't supposed to be mocked, so we use a thin wrapper. */
     internal interface ChoreographerWrapper {
         fun postFrameCallback(callback: Choreographer.FrameCallback)
+
         fun removeFrameCallback(callback: Choreographer.FrameCallback)
     }
 
@@ -744,10 +737,7 @@
 
     internal open fun cancelCoroutineScopesInOnDestroy() = true
 
-    /**
-     * This is open for use by tests, it allows them to inject a custom [SurfaceHolder].
-     *
-     */
+    /** This is open for use by tests, it allows them to inject a custom [SurfaceHolder]. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public open fun getWallpaperSurfaceHolderOverride(): SurfaceHolder? = null
 
@@ -1402,10 +1392,11 @@
                     // probably will connect at a later time. In the latter case we should
                     // register a parameterless engine to allow the subsequent connection to
                     // succeed.
-                    pendingWallpaperInstance = InteractiveInstanceManager
-                        .setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance(
-                            this
-                        )
+                    pendingWallpaperInstance =
+                        InteractiveInstanceManager
+                            .setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance( // ktlint-disable max-line-length
+                                this
+                            )
                 }
 
                 // If there's a pending WallpaperInteractiveWatchFaceInstance then create it.
@@ -1460,7 +1451,7 @@
         @SuppressWarnings("NewApi")
         internal fun attachToParameterlessEngine(
             pendingWallpaperInstance:
-            InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance
+                InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance
         ) {
             uiThreadCoroutineScope.launch {
                 try {
@@ -2241,12 +2232,7 @@
             watchState: WatchState
         ) {
             val broadcastsObserver =
-                BroadcastsObserver(
-                    watchState,
-                    this,
-                    deferredWatchFaceImpl,
-                    uiThreadCoroutineScope
-                )
+                BroadcastsObserver(watchState, this, deferredWatchFaceImpl, uiThreadCoroutineScope)
 
             // There's no point creating BroadcastsReceiver or listening for Accessibility state
             // changes if this is a headless instance.
@@ -2881,7 +2867,6 @@
 /**
  * If the instance ID for [MutableWatchState.watchFaceInstanceId] begin with this prefix, then the
  * system sends consistent IDs for interactive, headless and editor sessions.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 const val SYSTEM_SUPPORTS_CONSISTENT_IDS_PREFIX = "wfId-"
@@ -2889,14 +2874,12 @@
 /**
  * Instance ID to use when either there's no system id or it doesn't start with
  * [SYSTEM_SUPPORTS_CONSISTENT_IDS_PREFIX].
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) const val DEFAULT_INSTANCE_ID = "defaultInstance"
 
 /**
  * This is needed to make the instance id consistent between Interactive, Headless and EditorSession
  * for old versions of the system.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 fun sanitizeWatchFaceId(instanceId: String?) =
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index b2ef214..29ebfde 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -175,6 +175,7 @@
         set(@Px value) {
             field = value
         }
+
     public var isHeadless: Boolean = false
 
     public fun asWatchState(): WatchState =
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index 1dbbc9a..d6e5cdc 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -73,10 +73,10 @@
         }
 
         /**
-         * We either return the pendingWallpaperInteractiveWatchFaceInstance if there is one or
-         * set parameterlessEngine. A parameterless engine, is one that's been created without any
-         * start up params. Typically this can only happen if a WSL watchface is upgraded to an
-         * androidx one, so WallpaperManager knows about it but WearServices/WSL does not.
+         * We either return the pendingWallpaperInteractiveWatchFaceInstance if there is one or set
+         * parameterlessEngine. A parameterless engine, is one that's been created without any start
+         * up params. Typically this can only happen if a WSL watchface is upgraded to an androidx
+         * one, so WallpaperManager knows about it but WearServices/WSL does not.
          */
         @SuppressLint("SyntheticAccessor")
         fun setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt
index e3abf0a..f2312e1e 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt
@@ -82,4 +82,4 @@
     override fun close() {
         host.release()
     }
-}
\ No newline at end of file
+}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index b41c5d19..92a7cf0 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -49,10 +49,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.MainScope
 
-/**
- * A service for creating and controlling watch face instances.
- *
- */
+/** A service for creating and controlling watch face instances. */
 @RequiresApi(27)
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public open class WatchFaceControlService : Service() {
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt
index 71b0d50..0755492 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt
@@ -22,10 +22,7 @@
 import androidx.wear.watchface.IndentingPrintWriter
 import androidx.wear.watchface.editor.data.EditorStateWireFormat
 
-/**
- * Implementation of [IEditorService], intended for use by EditorSession only.
- *
- */
+/** Implementation of [IEditorService], intended for use by EditorSession only. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class EditorService : IEditorService.Stub() {
     private val lock = Any()
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index fa8a2c8..7296865 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -71,6 +71,7 @@
     /** The ids of the [ComplicationSlot]s that have been tapped. */
     val tappedComplicationSlotIds: List<Int>
         get() = mutableTappedComplicationIds
+
     var complicationSelected: Int? = null
     var mockZoneId: ZoneId = ZoneId.of("UTC")
     var renderer: Renderer? = null
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index e7f10f7..bba42d55 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -23,6 +23,8 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.ServiceInfo
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.Color
@@ -39,8 +41,6 @@
 import android.provider.Settings
 import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.ComplicationText as WireComplicationText
-import android.content.IntentFilter
-import android.content.pm.ServiceInfo
 import android.support.wearable.watchface.Constants
 import android.support.wearable.watchface.IWatchFaceService
 import android.support.wearable.watchface.WatchFaceStyle
@@ -3760,21 +3760,22 @@
     @Config(sdk = [Build.VERSION_CODES.R])
     public fun directBoot() {
         val instanceId = "DirectBootInstance"
-        val params = WallpaperInteractiveWatchFaceInstanceParams(
-            instanceId,
-            DeviceConfig(false, false, 0, 0),
-            WatchUiState(false, 0),
-            UserStyle(
-                hashMapOf(
-                    colorStyleSetting to blueStyleOption,
-                    watchHandStyleSetting to gothicStyleOption
-                )
+        val params =
+            WallpaperInteractiveWatchFaceInstanceParams(
+                instanceId,
+                DeviceConfig(false, false, 0, 0),
+                WatchUiState(false, 0),
+                UserStyle(
+                        hashMapOf(
+                            colorStyleSetting to blueStyleOption,
+                            watchHandStyleSetting to gothicStyleOption
+                        )
+                    )
+                    .toWireFormat(),
+                null,
+                null,
+                null
             )
-                .toWireFormat(),
-            null,
-            null,
-            null
-        )
         testWatchFaceService =
             TestWatchFaceService(
                 WatchFaceType.ANALOG,
@@ -5215,19 +5216,19 @@
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(999))
         assertThat(getLeftShortTextComplicationDataText()).isEqualTo("A")
         assertThat(
-            engineWrapper.contentDescriptionLabels[1]
-                .text
-                .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
-        )
+                engineWrapper.contentDescriptionLabels[1]
+                    .text
+                    .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+            )
             .isEqualTo("A")
 
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(1000))
         assertThat(getLeftShortTextComplicationDataText()).isEqualTo("B")
         assertThat(
-            engineWrapper.contentDescriptionLabels[1]
-                .text
-                .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
-        )
+                engineWrapper.contentDescriptionLabels[1]
+                    .text
+                    .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+            )
             .isEqualTo("B")
 
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(1999))
@@ -5236,10 +5237,10 @@
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(2000))
         assertThat(getLeftShortTextComplicationDataText()).isEqualTo("C")
         assertThat(
-            engineWrapper.contentDescriptionLabels[1]
-                .text
-                .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
-        )
+                engineWrapper.contentDescriptionLabels[1]
+                    .text
+                    .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+            )
             .isEqualTo("C")
 
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(2999))
@@ -5248,10 +5249,10 @@
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(3000))
         assertThat(getLeftShortTextComplicationDataText()).isEqualTo("B")
         assertThat(
-            engineWrapper.contentDescriptionLabels[1]
-                .text
-                .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
-        )
+                engineWrapper.contentDescriptionLabels[1]
+                    .text
+                    .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+            )
             .isEqualTo("B")
 
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(3999))
@@ -5260,10 +5261,10 @@
         complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(4000))
         assertThat(getLeftShortTextComplicationDataText()).isEqualTo("A")
         assertThat(
-            engineWrapper.contentDescriptionLabels[1]
-                .text
-                .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
-        )
+                engineWrapper.contentDescriptionLabels[1]
+                    .text
+                    .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+            )
             .isEqualTo("A")
     }
 
@@ -6556,13 +6557,14 @@
         lateinit var delegate: WatchFace.EditorDelegate
 
         val shadowPackageManager = shadowOf(context.packageManager)
-        val controlServiceComponent = ComponentName(
-            context, TestWatchFaceControlService::class.java
+        val controlServiceComponent =
+            ComponentName(context, TestWatchFaceControlService::class.java)
+        shadowPackageManager.addOrUpdateService(
+            ServiceInfo().apply {
+                packageName = controlServiceComponent.packageName
+                name = controlServiceComponent.className
+            }
         )
-        shadowPackageManager.addOrUpdateService(ServiceInfo().apply {
-            packageName = controlServiceComponent.packageName
-            name = controlServiceComponent.className
-        })
         shadowPackageManager.addIntentFilterForService(
             controlServiceComponent,
             IntentFilter(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE)
@@ -6632,19 +6634,20 @@
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
         engineWrapper.onVisibilityChanged(true)
 
-        val callback = object : IPendingInteractiveWatchFace.Stub() {
-            override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
+        val callback =
+            object : IPendingInteractiveWatchFace.Stub() {
+                override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
 
-            override fun onInteractiveWatchFaceCreated(
-                iInteractiveWatchFace: IInteractiveWatchFace
-            ) {
-                interactiveWatchFaceInstance = iInteractiveWatchFace
-            }
+                override fun onInteractiveWatchFaceCreated(
+                    iInteractiveWatchFace: IInteractiveWatchFace
+                ) {
+                    interactiveWatchFaceInstance = iInteractiveWatchFace
+                }
 
-            override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
-                fail("WatchFace crashed: $exception")
+                override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
+                    fail("WatchFace crashed: $exception")
+                }
             }
-        }
 
         InteractiveInstanceManager
             .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
diff --git a/webkit/OWNERS b/webkit/OWNERS
index 3852064..93f399a 100644
--- a/webkit/OWNERS
+++ b/webkit/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 461230
+elabadysayed@google.com
 ntfschr@google.com
 pbirk@google.com
 swestphal@google.com
diff --git a/window/window-testing/api/api_lint.ignore b/window/window-testing/api/api_lint.ignore
index 1789662..f64ca92 100644
--- a/window/window-testing/api/api_lint.ignore
+++ b/window/window-testing/api/api_lint.ignore
@@ -1,9 +1,9 @@
 // Baseline format: 1.0
-InvalidNullabilityOverride: androidx.window.testing.layout.StubWindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #0:
-    Invalid nullability on parameter `base` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.window.testing.layout.StubWindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #1:
-    Invalid nullability on parameter `description` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
 InvalidNullabilityOverride: androidx.window.testing.layout.WindowLayoutInfoPublisherRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #0:
     Invalid nullability on parameter `base` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
 InvalidNullabilityOverride: androidx.window.testing.layout.WindowLayoutInfoPublisherRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #1:
     Invalid nullability on parameter `description` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: androidx.window.testing.layout.WindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #0:
+    Invalid nullability on parameter `base` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: androidx.window.testing.layout.WindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #1:
+    Invalid nullability on parameter `description` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
diff --git a/window/window-testing/api/current.txt b/window/window-testing/api/current.txt
index 12a7d20..0c67563 100644
--- a/window/window-testing/api/current.txt
+++ b/window/window-testing/api/current.txt
@@ -7,6 +7,11 @@
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -20,5 +25,10 @@
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
   }
 
+  public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public WindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
 }
 
diff --git a/window/window-testing/api/public_plus_experimental_current.txt b/window/window-testing/api/public_plus_experimental_current.txt
index 16174f8..bd1d758 100644
--- a/window/window-testing/api/public_plus_experimental_current.txt
+++ b/window/window-testing/api/public_plus_experimental_current.txt
@@ -41,16 +41,11 @@
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
-    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
-    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
-    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
-  }
-
-  @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
-    ctor public StubWindowMetricsCalculatorRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -64,5 +59,10 @@
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
   }
 
+  public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public WindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
 }
 
diff --git a/window/window-testing/api/restricted_current.txt b/window/window-testing/api/restricted_current.txt
index 12a7d20..0c67563 100644
--- a/window/window-testing/api/restricted_current.txt
+++ b/window/window-testing/api/restricted_current.txt
@@ -7,6 +7,11 @@
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -20,5 +25,10 @@
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
   }
 
+  public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public WindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
 }
 
diff --git a/window/window-testing/src/androidTest/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRuleTest.kt b/window/window-testing/src/androidTest/java/androidx/window/testing/layout/WindowMetricsCalculatorRuleTest.kt
similarity index 94%
rename from window/window-testing/src/androidTest/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRuleTest.kt
rename to window/window-testing/src/androidTest/java/androidx/window/testing/layout/WindowMetricsCalculatorRuleTest.kt
index 3e6544d..a663eaf 100644
--- a/window/window-testing/src/androidTest/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRuleTest.kt
+++ b/window/window-testing/src/androidTest/java/androidx/window/testing/layout/WindowMetricsCalculatorRuleTest.kt
@@ -23,7 +23,6 @@
 import androidx.annotation.RequiresApi
 import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.WindowMetricsCalculator
 import androidx.window.testing.TestActivity
 import org.junit.Assert.assertEquals
@@ -35,19 +34,18 @@
 import org.junit.runners.model.Statement
 
 /**
- * A test class for [StubWindowMetricsCalculatorRule] that tests using
+ * A test class for [WindowMetricsCalculatorRule] that tests using
  * [StubWindowMetricsCalculator] instead of the actual implementation.
  */
-@OptIn(ExperimentalWindowApi::class)
-class StubWindowMetricsCalculatorRuleTest {
+class WindowMetricsCalculatorRuleTest {
     private val activityRule = ActivityScenarioRule(TestActivity::class.java)
-    private val stubWindowMetricsCalculatorRule = StubWindowMetricsCalculatorRule()
+    private val mWindowMetricsCalculatorRule = WindowMetricsCalculatorRule()
 
     @get:Rule
     val testRule: TestRule
 
     init {
-        testRule = RuleChain.outerRule(stubWindowMetricsCalculatorRule).around(activityRule)
+        testRule = RuleChain.outerRule(mWindowMetricsCalculatorRule).around(activityRule)
     }
 
     @Test
@@ -166,7 +164,7 @@
             WindowMetricsCalculator.reset()
             val expected = WindowMetricsCalculator.getOrCreate()
             try {
-                StubWindowMetricsCalculatorRule().apply(
+                WindowMetricsCalculatorRule().apply(
                     object : Statement() {
                         override fun evaluate() {
                             throw TestException
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt
index b6aa95e..2a065cb 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt
@@ -19,7 +19,6 @@
 
 import android.app.Activity
 import android.graphics.Rect
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.FoldingFeature
 import androidx.window.layout.FoldingFeature.OcclusionType.Companion.FULL
 import androidx.window.layout.FoldingFeature.OcclusionType.Companion.NONE
@@ -96,7 +95,6 @@
  */
 @JvmOverloads
 @JvmName("createFoldingFeature")
-@ExperimentalWindowApi
 fun FoldingFeature(
     windowBounds: Rect,
     center: Int = -1,
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt
index 14bf0c7..7a8eeb2 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt
@@ -16,14 +16,12 @@
 
 package androidx.window.testing.layout
 
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.WindowMetricsCalculator
 import androidx.window.layout.WindowMetricsCalculatorDecorator
 
 /**
  * A decorator to return [StubWindowMetricsCalculator] instead of the actual implementation.
  */
-@ExperimentalWindowApi
 internal object StubMetricDecorator : WindowMetricsCalculatorDecorator {
     override fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator {
         return StubWindowMetricsCalculator()
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt
index c9c7aeb..b8d4b070 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt
@@ -45,7 +45,7 @@
  *     <li>A fold in the middle and has horizontal orientation.</li>
  * </ul>
  */
-public class WindowLayoutInfoPublisherRule() : TestRule {
+class WindowLayoutInfoPublisherRule : TestRule {
 
     private val flow = MutableSharedFlow<WindowLayoutInfo>(
         extraBufferCapacity = 1,
@@ -70,7 +70,7 @@
      * Send an arbitrary [WindowLayoutInfo] through
      * [androidx.window.layout.WindowInfoTracker.windowLayoutInfo]. Each event is sent only once.
      */
-    public fun overrideWindowLayoutInfo(info: WindowLayoutInfo) {
+    fun overrideWindowLayoutInfo(info: WindowLayoutInfo) {
         flow.tryEmit(info)
     }
 }
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRule.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowMetricsCalculatorRule.kt
similarity index 93%
rename from window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRule.kt
rename to window/window-testing/src/main/java/androidx/window/testing/layout/WindowMetricsCalculatorRule.kt
index bfb7c48..10e68f1 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRule.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowMetricsCalculatorRule.kt
@@ -17,7 +17,6 @@
 package androidx.window.testing.layout
 
 import android.app.Activity
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.WindowMetricsCalculator
 import org.junit.rules.TestRule
 import org.junit.runner.Description
@@ -32,8 +31,7 @@
  * the Espresso Test framework with an actual [Activity] and use the actual
  * [WindowMetricsCalculator].
  */
-@ExperimentalWindowApi
-class StubWindowMetricsCalculatorRule : TestRule {
+class WindowMetricsCalculatorRule : TestRule {
 
     override fun apply(base: Statement, description: Description): Statement {
         return object : Statement() {
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 5617dbb..8fbbb45 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -302,6 +302,7 @@
 
   public interface WindowInfoTracker {
     method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
     method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
     field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
   }
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index 0ac0175..0c1d735 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -330,7 +330,7 @@
 
   public interface WindowInfoTracker {
     method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
-    method @androidx.window.core.ExperimentalWindowApi public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+    method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
     method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
     field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
   }
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 5617dbb..8fbbb45 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -302,6 +302,7 @@
 
   public interface WindowInfoTracker {
     method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
     method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
     field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
   }
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
index 3b93a9a..1eab9d0 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
@@ -18,9 +18,13 @@
 
 import android.util.Log
 import androidx.window.core.ConsumerAdapter
+import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.WindowExtensions
+import androidx.window.extensions.WindowExtensions.VENDOR_API_LEVEL_1
 import androidx.window.extensions.WindowExtensionsProvider
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
 import org.junit.Test
 
 /**
@@ -31,14 +35,13 @@
  */
 class SafeActivityEmbeddingComponentProviderTest {
 
+    // TODO(b/267708462) : add a more reliable test
     /**
      * Test that if [WindowExtensionsProvider] is available then
      * use [SafeActivityEmbeddingComponentProvider.activityEmbeddingComponent] to validate.
      * If [WindowExtensions.getActivityEmbeddingComponent] matches contract,
      * return a non-null value.
      * If it doesn't match, it will return a null.
-     *
-     *  TODO(b/267708462) : add a more reliable test
      */
     @Test
     fun activityEmbeddingComponentIsAvailable_ifProviderIsAvailable() {
@@ -50,12 +53,12 @@
             Log.d(TAG, "Device doesn't have WindowExtensions available")
             return
         }
-        val safeComponent = SafeActivityEmbeddingComponentProvider(
+        val safeProvider = SafeActivityEmbeddingComponentProvider(
             loader,
             consumerAdapter,
             windowExtensions
         )
-            .activityEmbeddingComponent
+        val safeComponent = safeProvider.activityEmbeddingComponent
         try {
             val actualComponent = windowExtensions.activityEmbeddingComponent
             if (actualComponent == null) {
@@ -63,8 +66,11 @@
             } else {
                 // TODO(b/267573854) : verify upon each api level
                 // TODO(b/267708462) : more reliable test for testing actual method matching
-                if (safeComponent == null) {
-                    Log.d(TAG, "ActivityEmbeddingComponent on device doesn't match our constraints")
+                assertNotNull(safeComponent)
+                assertTrue(safeProvider.isActivityEmbeddingComponentAccessible())
+                when (ExtensionsUtil.safeVendorApiLevel) {
+                    VENDOR_API_LEVEL_1 -> assertTrue(safeProvider.hasValidVendorApiLevel1())
+                    else -> assertTrue(safeProvider.hasValidVendorApiLevel2())
                 }
             }
         } catch (e: UnsupportedOperationException) {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
index bc78a38..9855ce8 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
@@ -16,10 +16,13 @@
 
 package androidx.window.layout
 
-import android.util.Log
 import androidx.window.core.ConsumerAdapter
+import androidx.window.core.ExtensionsUtil
+import androidx.window.extensions.WindowExtensions.VENDOR_API_LEVEL_1
 import androidx.window.extensions.WindowExtensionsProvider
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
 import org.junit.Test
 
 /**
@@ -37,8 +40,8 @@
     fun windowLayoutComponentIsAvailable_ifProviderIsAvailable() {
         val loader = SafeWindowLayoutComponentProviderTest::class.java.classLoader!!
         val consumerAdapter = ConsumerAdapter(loader)
-        val safeComponent = SafeWindowLayoutComponentProvider(loader, consumerAdapter)
-            .windowLayoutComponent
+        val safeProvider = SafeWindowLayoutComponentProvider(loader, consumerAdapter)
+        val safeComponent = safeProvider.windowLayoutComponent
 
         try {
             val extensions = WindowExtensionsProvider.getWindowExtensions()
@@ -48,15 +51,16 @@
             } else {
                 // TODO(b/267831038): verify upon each api level
                 // TODO(b/267708462): more reliable test for testing actual method matching
-                Log.d(TAG, "WindowLayoutComponent on device doesn't match our constraints")
+                assertNotNull(safeComponent)
+                assertTrue(safeProvider.isWindowLayoutComponentAccessible())
+                when (ExtensionsUtil.safeVendorApiLevel) {
+                    VENDOR_API_LEVEL_1 -> assertTrue(safeProvider.hasValidVendorApiLevel1())
+                    else -> assertTrue(safeProvider.hasValidVendorApiLevel2())
+                }
             }
         } catch (e: UnsupportedOperationException) {
             // Invalid implementation of extensions
             assertNull(safeComponent)
         }
     }
-
-    companion object {
-        private const val TAG = "SafeWindowLayoutComponentProviderTest"
-    }
 }
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt b/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
new file mode 100644
index 0000000..2aa82f4
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 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.window
+
+import androidx.window.reflection.ReflectionUtils
+import androidx.window.reflection.ReflectionUtils.doesReturn
+import androidx.window.reflection.ReflectionUtils.isPublic
+import androidx.window.reflection.WindowExtensionsConstants
+
+internal class SafeWindowExtensionsProvider(private val loader: ClassLoader) {
+    internal val windowExtensionsClass: Class<*>
+        get() {
+            return loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS)
+        }
+
+    internal fun isWindowExtensionsValid(): Boolean {
+        return isWindowExtensionsPresent() &&
+            ReflectionUtils.validateReflection(
+                "WindowExtensionsProvider#getWindowExtensions is not valid"
+            ) {
+                val providerClass = windowExtensionsProviderClass
+                val getWindowExtensionsMethod = providerClass
+                    .getDeclaredMethod("getWindowExtensions")
+                val windowExtensionsClass = windowExtensionsClass
+                getWindowExtensionsMethod.doesReturn(windowExtensionsClass) &&
+                    getWindowExtensionsMethod.isPublic
+            }
+    }
+
+    private fun isWindowExtensionsPresent(): Boolean {
+        return ReflectionUtils.checkIsPresent {
+            loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS)
+        }
+    }
+    private val windowExtensionsProviderClass: Class<*>
+        get() {
+            return loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS)
+        }
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt b/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
index 44699d8..92965c3 100644
--- a/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
@@ -17,6 +17,7 @@
 package androidx.window.embedding
 
 import android.app.Activity
+import androidx.annotation.VisibleForTesting
 import androidx.window.core.ConsumerAdapter
 import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.WindowExtensions
@@ -26,9 +27,8 @@
 import androidx.window.reflection.ReflectionUtils.doesReturn
 import androidx.window.reflection.ReflectionUtils.isPublic
 import androidx.window.reflection.ReflectionUtils.validateReflection
+import androidx.window.SafeWindowExtensionsProvider
 import androidx.window.reflection.WindowExtensionsConstants.ACTIVITY_EMBEDDING_COMPONENT_CLASS
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS
 
 /**
  * Reflection Guard for [ActivityEmbeddingComponent].
@@ -40,6 +40,8 @@
     private val consumerAdapter: ConsumerAdapter,
     private val windowExtensions: WindowExtensions
 ) {
+    private val safeWindowExtensionsProvider = SafeWindowExtensionsProvider(loader)
+
     val activityEmbeddingComponent: ActivityEmbeddingComponent?
         get() {
             return if (canUseActivityEmbeddingComponent()) {
@@ -54,7 +56,7 @@
         }
 
     private fun canUseActivityEmbeddingComponent(): Boolean {
-        if (!isWindowExtensionsValid() || !isActivityEmbeddingComponentValid()) {
+        if (!isActivityEmbeddingComponentAccessible()) {
             return false
         }
         // TODO(b/267573854) : update logic to fallback to lower version
@@ -67,6 +69,11 @@
         }
     }
 
+    @VisibleForTesting
+    internal fun isActivityEmbeddingComponentAccessible(): Boolean =
+        safeWindowExtensionsProvider.isWindowExtensionsValid() &&
+            isActivityEmbeddingComponentValid()
+
     /**
      * [WindowExtensions.VENDOR_API_LEVEL_1] includes the following methods:
      *  - [ActivityEmbeddingComponent.setEmbeddingRules]
@@ -78,7 +85,8 @@
      *  - [androidx.window.extensions.embedding.SplitPlaceholderRule]
      *  - [androidx.window.extensions.embedding.SplitInfo]
      */
-    private fun hasValidVendorApiLevel1(): Boolean {
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel1(): Boolean {
         return isMethodSetEmbeddingRulesValid() &&
             isMethodIsActivityEmbeddedValid() &&
             isMethodSetSplitInfoCallbackJavaConsumerValid()
@@ -93,7 +101,8 @@
      * and following classes: TODO(b/268583307) : add guard function for those classes
      *  - [androidx.window.extensions.embedding.SplitAttributes]
      */
-    private fun hasValidVendorApiLevel2(): Boolean {
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel2(): Boolean {
         return hasValidVendorApiLevel1() &&
             isMethodSetSplitInfoCallbackWindowConsumerValid() &&
             isMethodClearSplitInfoCallbackValid() &&
@@ -166,20 +175,9 @@
         }
     }
 
-    private fun isWindowExtensionsValid(): Boolean {
-        return validateReflection("WindowExtensionsProvider#getWindowExtensions is not valid") {
-            val providerClass = windowExtensionsProviderClass
-            val getWindowExtensionsMethod = providerClass.getDeclaredMethod("getWindowExtensions")
-            val windowExtensionsClass = windowExtensionsClass
-            getWindowExtensionsMethod.isPublic && getWindowExtensionsMethod.doesReturn(
-                windowExtensionsClass
-            )
-        }
-    }
-
     private fun isActivityEmbeddingComponentValid(): Boolean {
         return validateReflection("WindowExtensions#getActivityEmbeddingComponent is not valid") {
-            val extensionsClass = windowExtensionsClass
+            val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
             val getActivityEmbeddingComponentMethod =
                 extensionsClass.getMethod("getActivityEmbeddingComponent")
             val activityEmbeddingComponentClass = activityEmbeddingComponentClass
@@ -188,16 +186,6 @@
         }
     }
 
-    private val windowExtensionsProviderClass: Class<*>
-        get() {
-            return loader.loadClass(WINDOW_EXTENSIONS_PROVIDER_CLASS)
-        }
-
-    private val windowExtensionsClass: Class<*>
-        get() {
-            return loader.loadClass(WINDOW_EXTENSIONS_CLASS)
-        }
-
     private val activityEmbeddingComponentClass: Class<*>
         get() {
             return loader.loadClass(ACTIVITY_EMBEDDING_COMPONENT_CLASS)
diff --git a/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt b/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
index bc0dac5..b9dcbef 100644
--- a/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
@@ -19,21 +19,20 @@
 import android.app.Activity
 import android.content.Context
 import android.graphics.Rect
+import androidx.annotation.VisibleForTesting
 import androidx.window.core.ConsumerAdapter
 import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.WindowExtensions
 import androidx.window.extensions.WindowExtensionsProvider
 import androidx.window.extensions.core.util.function.Consumer
 import androidx.window.extensions.layout.WindowLayoutComponent
-import androidx.window.reflection.ReflectionUtils.checkIsPresent
 import androidx.window.reflection.ReflectionUtils.doesReturn
 import androidx.window.reflection.ReflectionUtils.isPublic
 import androidx.window.reflection.ReflectionUtils.validateReflection
+import androidx.window.SafeWindowExtensionsProvider
 import androidx.window.reflection.WindowExtensionsConstants.FOLDING_FEATURE_CLASS
 import androidx.window.reflection.WindowExtensionsConstants.JAVA_CONSUMER
 import androidx.window.reflection.WindowExtensionsConstants.WINDOW_CONSUMER
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS
 import androidx.window.reflection.WindowExtensionsConstants.WINDOW_LAYOUT_COMPONENT_CLASS
 
 /**
@@ -45,6 +44,7 @@
     private val loader: ClassLoader,
     private val consumerAdapter: ConsumerAdapter
 ) {
+    private val safeWindowExtensionsProvider = SafeWindowExtensionsProvider(loader)
 
     val windowLayoutComponent: WindowLayoutComponent?
         get() {
@@ -60,10 +60,7 @@
         }
 
     private fun canUseWindowLayoutComponent(): Boolean {
-        if (!isWindowExtensionsPresent() || !isWindowExtensionsValid() ||
-            !isWindowLayoutProviderValid() ||
-            !isFoldingFeatureValid()
-        ) {
+        if (!isWindowLayoutComponentAccessible()) {
             return false
         }
         // TODO(b/267831038): can fallback to VendorApiLevel1 when level2 is not match
@@ -76,11 +73,11 @@
         }
     }
 
-    private fun isWindowExtensionsPresent(): Boolean {
-        return checkIsPresent {
-            loader.loadClass(WINDOW_EXTENSIONS_PROVIDER_CLASS)
-        }
-    }
+    @VisibleForTesting
+    internal fun isWindowLayoutComponentAccessible(): Boolean =
+        safeWindowExtensionsProvider.isWindowExtensionsValid() &&
+            isWindowLayoutProviderValid() &&
+            isFoldingFeatureValid()
 
     /**
      * [WindowExtensions.VENDOR_API_LEVEL_1] includes the following methods
@@ -88,35 +85,24 @@
      * [java.util.function.Consumer]
      *  - [WindowLayoutComponent.removeWindowLayoutInfoListener] with [java.util.function.Consumer]
      */
-    private fun hasValidVendorApiLevel1(): Boolean {
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel1(): Boolean {
         return isMethodWindowLayoutInfoListenerJavaConsumerValid()
     }
 
     /**
      * [WindowExtensions.VENDOR_API_LEVEL_2] includes the following methods
-     *  - [WindowLayoutComponent.addWindowLayoutInfoListener] with [Context] and
-     * [java.util.function.Consumer]
      *  - [WindowLayoutComponent.addWindowLayoutInfoListener] with [Context] and [Consumer]
      *  - [WindowLayoutComponent.removeWindowLayoutInfoListener] with [Consumer]
      */
-    private fun hasValidVendorApiLevel2(): Boolean {
-        return hasValidVendorApiLevel1() &&
-            isMethodWindowLayoutInfoListenerWindowConsumerValid()
-    }
-
-    private fun isWindowExtensionsValid(): Boolean {
-        return validateReflection("WindowExtensionsProvider#getWindowExtensions is not valid") {
-            val providerClass = windowExtensionsProviderClass
-            val getWindowExtensionsMethod = providerClass.getDeclaredMethod("getWindowExtensions")
-            val windowExtensionsClass = windowExtensionsClass
-            getWindowExtensionsMethod.doesReturn(windowExtensionsClass) &&
-                getWindowExtensionsMethod.isPublic
-        }
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel2(): Boolean {
+        return hasValidVendorApiLevel1() && isMethodWindowLayoutInfoListenerWindowConsumerValid()
     }
 
     private fun isWindowLayoutProviderValid(): Boolean {
         return validateReflection("WindowExtensions#getWindowLayoutComponent is not valid") {
-            val extensionsClass = windowExtensionsClass
+            val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
             val getWindowLayoutComponentMethod =
                 extensionsClass.getMethod("getWindowLayoutComponent")
             val windowLayoutComponentClass = windowLayoutComponentClass
@@ -178,16 +164,6 @@
         }
     }
 
-    private val windowExtensionsProviderClass: Class<*>
-        get() {
-            return loader.loadClass(WINDOW_EXTENSIONS_PROVIDER_CLASS)
-        }
-
-    private val windowExtensionsClass: Class<*>
-        get() {
-            return loader.loadClass(WINDOW_EXTENSIONS_CLASS)
-        }
-
     private val foldingFeatureClass: Class<*>
         get() {
             return loader.loadClass(FOLDING_FEATURE_CLASS)
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
index 8af11e0..7ecff91 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
@@ -24,7 +24,6 @@
 import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
 import androidx.annotation.UiContext
 import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.adapter.WindowBackend
 import androidx.window.layout.adapter.extensions.ExtensionWindowLayoutInfoBackend
 import androidx.window.layout.adapter.sidecar.SidecarWindowBackend
@@ -62,9 +61,9 @@
      * @throws NotImplementedError when [Context] is not an [UiContext] or this method has no
      * supporting implementation.
      */
-    @ExperimentalWindowApi
     fun windowLayoutInfo(@UiContext context: Context): Flow<WindowLayoutInfo> {
-        val windowLayoutInfoFlow: Flow<WindowLayoutInfo>? = windowLayoutInfo((context as Activity))
+        val windowLayoutInfoFlow: Flow<WindowLayoutInfo>? = (context as? Activity)
+            ?.let { activity -> windowLayoutInfo(activity) }
         return windowLayoutInfoFlow
             ?: throw NotImplementedError(
                 message = "Must override windowLayoutInfo(context) and provide an implementation.")
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
index 2247c4b..26dd99b 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
@@ -65,8 +65,4 @@
             }
         }
     }
-
-    internal companion object {
-        private const val BUFFER_CAPACITY = 10
-    }
 }
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
index b0a0b73..8d8151e 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
@@ -26,7 +26,6 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.UiContext
 import androidx.core.view.WindowInsetsCompat
-import androidx.window.core.ExperimentalWindowApi
 
 /**
  * An interface to calculate the [WindowMetrics] for an [Activity] or a [UiContext].
@@ -131,14 +130,12 @@
             return decorator(WindowMetricsCalculatorCompat)
         }
 
-        @ExperimentalWindowApi
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         fun overrideDecorator(overridingDecorator: WindowMetricsCalculatorDecorator) {
             decorator = overridingDecorator::decorate
         }
 
-        @ExperimentalWindowApi
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         fun reset() {
@@ -159,7 +156,6 @@
     }
 }
 
-@ExperimentalWindowApi
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 interface WindowMetricsCalculatorDecorator {
 
diff --git a/work/integration-tests/testapp/build.gradle b/work/integration-tests/testapp/build.gradle
index a44e9ab..3984082 100644
--- a/work/integration-tests/testapp/build.gradle
+++ b/work/integration-tests/testapp/build.gradle
@@ -61,7 +61,7 @@
     implementation(project(":work:work-multiprocess"))
     implementation(project(":work:work-gcm"))
     implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
-    implementation("androidx.arch.core:core-runtime:2.1.0")
+    implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation("androidx.recyclerview:recyclerview:1.1.0")
     implementation(libs.material)
 }
diff --git a/work/work-runtime/build.gradle b/work/work-runtime/build.gradle
index 0cc27df..5e8d67b 100644
--- a/work/work-runtime/build.gradle
+++ b/work/work-runtime/build.gradle
@@ -76,7 +76,7 @@
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
-    androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.espressoCore)
diff --git a/work/work-testing/build.gradle b/work/work-testing/build.gradle
index a4e7df7..45e33ef 100644
--- a/work/work-testing/build.gradle
+++ b/work/work-testing/build.gradle
@@ -27,7 +27,7 @@
     implementation("androidx.lifecycle:lifecycle-livedata-core:2.5.1")
     implementation("androidx.room:room-runtime:2.5.0")
 
-    androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)