Merge "Adds a ScrollState param to dropdown menus" into androidx-main
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
index c81ae66..c7e2ad8 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
@@ -23,69 +23,63 @@
 
     @Test
     fun withAnEmptyFlag_itReturnsTheDefaultValue() {
-        assertThat(KmpFlagParser.parse("")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
-        )
+        assertThat(KmpFlagParser.parse("")).isEqualTo(setOf(KmpPlatform.JVM))
     }
 
     @Test
     fun withANullFlag_itReturnsTheDefaultValue() {
-        assertThat(KmpFlagParser.parse(null)).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
-        )
+        assertThat(KmpFlagParser.parse(null)).isEqualTo(setOf(KmpPlatform.JVM))
     }
 
     @Test
     fun withASingleDefaultPlatform_itParsesTheFlagCorrectly() {
-        assertThat(KmpFlagParser.parse("+jvm")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
-        )
+        assertThat(KmpFlagParser.parse("+jvm")).isEqualTo(setOf(KmpPlatform.JVM))
     }
 
     @Test
     fun withNoPlatforms_itParsesTheFlagCorrectly() {
-        assertThat(KmpFlagParser.parse("-jvm,-desktop")).isEqualTo(emptySet<KmpPlatform>())
+        assertThat(KmpFlagParser.parse("-jvm")).isEqualTo(emptySet<KmpPlatform>())
     }
 
     @Test
     fun withASingleNonDefaultPlatform_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("+js")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.DESKTOP)
+            setOf(KmpPlatform.JVM, KmpPlatform.JS)
         )
     }
 
     @Test
     fun withAMultiplePlatforms_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("+js,+mac")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.DESKTOP)
+            setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.MAC)
         )
     }
 
     @Test
     fun withNegativeFlags_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("-jvm,+mac")).isEqualTo(
-            setOf(KmpPlatform.MAC, KmpPlatform.DESKTOP)
+            setOf(KmpPlatform.MAC)
         )
     }
 
     @Test
     fun withTheNativeFlag_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("+native")).isEqualTo(
-            setOf(KmpPlatform.JVM, KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
+            setOf(KmpPlatform.JVM, KmpPlatform.MAC, KmpPlatform.LINUX)
         )
     }
 
     @Test
     fun withMultipleFlagsIncludingTheNativeFlag_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("-jvm,+native,+js")).isEqualTo(
-            setOf(KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
+            setOf(KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.LINUX)
         )
     }
 
     @Test
     fun withRedundentFlags_itParsesTheFlagCorrectly() {
         assertThat(KmpFlagParser.parse("-jvm,+native,+linux,+mac,+linux")).isEqualTo(
-            setOf(KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
+            setOf(KmpPlatform.MAC, KmpPlatform.LINUX)
         )
     }
 }
\ 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 f6c6252..6c1b432 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -86,17 +86,6 @@
         } 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 3cb172a..e16b300 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
@@ -206,11 +206,6 @@
 
     // 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 d6a11f1..b3be53b 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
@@ -23,7 +23,6 @@
 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
@@ -95,10 +94,6 @@
                     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 1b04cf7..590be01 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
@@ -20,7 +20,6 @@
 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
@@ -57,11 +56,4 @@
     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 7961e09..802d8ef 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
@@ -31,11 +31,10 @@
     // https://blog.jetbrains.com/kotlin/2021/10/important-ua-parser-js-exploit-and-kotlin-js/
     JS,
     MAC,
-    LINUX,
-    DESKTOP;
+    LINUX;
     companion object {
         val native = listOf(MAC, LINUX)
-        val enabledByDefault = listOf(JVM, DESKTOP)
+        val enabledByDefault = listOf(JVM)
         private const val JVM_PLATFORM = "jvm"
         private const val JS_PLATFORM = "js"
         private const val MAC_ARM_64 = "macosarm64"
@@ -44,7 +43,6 @@
         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)
@@ -90,5 +88,4 @@
 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 554c2fdf..326c96f 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
@@ -40,7 +40,6 @@
         @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 ef344d5..621ff22 100755
--- a/busytown/androidx.sh
+++ b/busytown/androidx.sh
@@ -22,8 +22,6 @@
       -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 dd31e36..5f39449 100755
--- a/busytown/androidx_compose_multiplatform.sh
+++ b/busytown/androidx_compose_multiplatform.sh
@@ -9,15 +9,10 @@
 
 
 # b/235340662 don't verify dependency versions because we cannot pin to multiplatform deps
-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 "$@"
+./androidx.sh \
+  -Pandroidx.compose.multiplatformEnabled=true \
+  compileDebugAndroidTestSources \
+  compileDebugSources \
+  desktopTestClasses \
+  -x verifyDependencyVersions  \
+  -Pandroidx.enableAffectedModuleDetection=false "$@"
diff --git a/busytown/androidx_multiplatform_linux.sh b/busytown/androidx_multiplatform_linux.sh
index 34e9b84..3f9d518 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 INFRAROGUE projects. This will also enable native targets.
-export ANDROIDX_PROJECTS=INFRAROGUE   # TODO: Switch from `INFRAROGUE` to `KMP`
+# build just KMP projects. This will also enable native targets.
+export ANDROIDX_PROJECTS=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 3373b9d..9f2dfbd 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=INFRAROGUE   # TODO: Switch from `INFRAROGUE` to `KMP`
+export ANDROIDX_PROJECTS=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 77eb725..8da2ed5 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=INFRAROGUE   # TODO: Switch from `INFRAROGUE` to `KMP`
+export ANDROIDX_PROJECTS=KMP
 
 # disable GCP cache, these machines don't have credentials.
 export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
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-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..51b1536 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
@@ -38,6 +38,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
@@ -144,7 +145,7 @@
         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.
@@ -175,9 +176,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 +192,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/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/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/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..630d3cf 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
@@ -30,6 +30,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;
@@ -102,10 +103,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."));
@@ -312,7 +314,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/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/streamsharing/StreamSharingTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
index 571a6b7..0ba6e394 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
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/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-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/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 8c83205..2ba09397e 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;
@@ -91,6 +90,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;
@@ -424,6 +424,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}
      *
      */
@@ -770,10 +795,17 @@
 
         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);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -1209,12 +1241,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 +1270,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 +1358,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 +1624,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
 
         /**
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/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 c38b355..f21dbb9 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,7 +219,6 @@
         ) { atEnd ->
             if (atEnd) overallDuration.toFloat() else 0f
         }
-        @Suppress("UnrememberedMutableState") // b/279909531
         return derivedStateOf { interpolate(timeState.value) }
     }
 
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index 46e9c83..a8baf7a 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-
-import androidx.build.AndroidXComposePlugin
-import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
 import androidx.build.RunApiTasks
 import androidx.build.BuildOnServerKt
@@ -29,55 +26,41 @@
     id("kotlin-multiplatform")
 }
 
-def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-
 dependencies {
 }
 
 kotlin {
-    if (desktopEnabled) {
-        jvm() {
-            withJava()
-        }
+    jvm() {
+        withJava()
     }
 
-    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)
-                }
-            }
+    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"))
         }
-    }
-}
 
-if (!desktopEnabled) {
-    kotlin {
-        jvm("disabled") {
-            withJava()
+        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)
+            }
         }
     }
 }
@@ -91,10 +74,8 @@
     }
 }
 
-if (desktopEnabled) {
-    tasks.findByName("jvmTest").configure {
-        systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
-    }
+tasks.findByName("jvmTest").configure {
+    systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
 }
 
 androidx {
@@ -146,6 +127,4 @@
     }
 }
 
-if (desktopEnabled) {
-    BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
-}
+BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 7502716..76b09be 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -15,105 +15,94 @@
  */
 
 import androidx.build.BuildOnServerKt
-import androidx.build.KmpPlatformsKt
 import androidx.build.SupportConfigKt
 
 plugins {
     id("AndroidXPlugin")
-    id("java")
     id("AndroidXComposePlugin")
     id("kotlin-multiplatform")
 }
 
-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")
+dependencies {
 }
 
-if (!desktopEnabled) {
-    kotlin {
-        jvm("disabled") {
-            withJava()
+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")
diff --git a/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt b/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt
deleted file mode 100644
index 0c32401..0000000
--- a/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt
+++ /dev/null
@@ -1 +0,0 @@
-// 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
deleted file mode 100644
index 0c32401..0000000
--- a/compose/desktop/desktop/src/disabled/Empty.kt
+++ /dev/null
@@ -1 +0,0 @@
-// 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/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/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/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/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index e1c695a..b2471cb 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -988,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;
   }
@@ -996,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 {
@@ -1007,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/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/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/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/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 37a15fa..f7317d7 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-import androidx.build.KmpPlatformsKt
+import androidx.build.AndroidXComposePlugin
 import androidx.build.LibraryType
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -23,95 +24,87 @@
     id("com.android.library")
 }
 
-def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
 
-androidXMultiplatform {
-    android()
-    if (desktopEnabled) desktop()
+dependencies {
 
-    sourceSets {
-        commonMain {
-            dependencies {
-                implementation(libs.kotlinStdlibCommon)
-                implementation(libs.kotlinCoroutinesCore)
-            }
-        }
+    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
+        /*
+         * When updating dependencies, make sure to make the an an analogous update in the
+         * corresponding block below
+         */
 
-        commonTest {
-            dependencies {
-                implementation kotlin("test")
-                implementation(libs.kotlinCoroutinesTest)
-            }
-        }
+        api(libs.kotlinCoroutinesAndroid)
 
-        jvmMain {
-            dependencies {
-                implementation(libs.kotlinStdlib)
-                api(libs.kotlinCoroutinesCore)
-            }
-        }
+        implementation("androidx.annotation:annotation:1.1.0")
+        implementation(libs.kotlinStdlib)
 
+        testImplementation(libs.kotlinTestJunit)
+        testImplementation(libs.junit)
+        testImplementation(libs.robolectric)
+        testImplementation(libs.kotlinCoroutinesTest)
 
-        androidMain {
-            dependsOn(jvmMain)
-            dependencies {
-                api(libs.kotlinCoroutinesAndroid)
-                api("androidx.annotation:annotation:1.1.0")
-            }
-        }
+        androidTestImplementation(libs.kotlinTestJunit)
+        androidTestImplementation(libs.testExtJunit)
+        androidTestImplementation(libs.testRules)
+        androidTestImplementation(libs.testRunner)
+        androidTestImplementation(libs.junit)
+        androidTestImplementation(libs.truth)
 
-        if (desktopEnabled) {
-            desktopMain {
-                dependsOn(jvmMain)
-            }
-        }
+        lintChecks(projectOrArtifact(":compose:runtime:runtime-lint"))
+        lintPublish(projectOrArtifact(":compose:runtime:runtime-lint"))
 
-        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)
-                dependsOn(nonEmulatorJvmTest)
-            }
-        }
+        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"))
+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 {
+                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 {
+                implementation kotlin("test")
+                implementation(libs.kotlinCoroutinesTest)
+            }
+
+            androidAndroidTest {
+                dependsOn(jvmTest)
+                dependencies {
+                    implementation(libs.testExtJunit)
+                    implementation(libs.testRules)
+                    implementation(libs.testRunner)
+                    implementation(libs.truth)
+                }
+            }
+        }
+    }
+    dependencies {
+        samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
+    }
 }
 
 android {
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/EffectsTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/LatchTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/LatchTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/LatchTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/LatchTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RestartTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RestartTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RestartTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RestartTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Contact.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Point.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Point.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Point.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Point.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Report.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Report.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Report.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Report.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/View.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/View.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Views.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
rename to compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
diff --git a/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt b/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
rename to compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
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/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/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..528a788 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
@@ -109,4 +109,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/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/RoundedPolygon.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
index 17c1fdd..95d2bff 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
@@ -228,13 +228,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 +241,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(
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/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/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/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-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 66641d0..aa1e705 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -195,8 +195,7 @@
     WEAR,
     GLANCE,
     TOOLS,
-    KMP, // All projects built as Kotlin Multi Platform (compose, datastore, collections, etc).
-    INFRAROGUE, // Projects built by playground team, mostly non-compose kmp.
+    KMP,
     CAMERA,
     NATIVE,
     WINDOW,
@@ -256,9 +255,6 @@
             case "KMP":
                 filter.add(BuildType.KMP)
                 break
-            case "INFRAROGUE":
-                filter.add(BuildType.INFRAROGUE)
-                break
             case "CAMERA":
                 filter.add(BuildType.CAMERA)
                 break
@@ -453,10 +449,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.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-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-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")
@@ -525,11 +521,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.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(":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(":compose:animation", [BuildType.COMPOSE])
 includeProject(":compose:animation:animation", [BuildType.COMPOSE])
 includeProject(":compose:animation:animation-lint", [BuildType.COMPOSE])
@@ -553,9 +549,9 @@
 includeProject(":compose:compiler:compiler-daemon:integration-tests", [BuildType.COMPOSE])
 
 if (isMultiplatformEnabled()) {
-    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:desktop", [BuildType.COMPOSE])
+    includeProject(":compose:desktop:desktop", [BuildType.COMPOSE])
+    includeProject(":compose:desktop:desktop:desktop-samples", "compose/desktop/desktop/samples", [BuildType.COMPOSE])
 }
 includeProject(":compose:foundation", [BuildType.COMPOSE])
 includeProject(":compose:foundation:foundation", [BuildType.COMPOSE])
@@ -605,7 +601,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, BuildType.KMP])
+includeProject(":compose:runtime:runtime", [BuildType.COMPOSE])
 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])
@@ -691,20 +687,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.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", [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-compose-samples", [BuildType.COMPOSE])
-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(":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(":documentfile:documentfile", [BuildType.MAIN])
 includeProject(":draganddrop:draganddrop", [BuildType.MAIN])
 includeProject(":draganddrop:integration-tests:sampleapp", [BuildType.MAIN])
@@ -1123,14 +1119,14 @@
 /////////////////////////////
 
 includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
-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-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-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.INFRAROGUE, BuildType.KMP, BuildType.COMPOSE])
+includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, 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])
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/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)