Merge "[Tabs] Add primary/secondary indicators" into androidx-main
diff --git a/.github/actions/build-single-project/action.yml b/.github/actions/build-single-project/action.yml
index ab4317f1..19a8cb4 100644
--- a/.github/actions/build-single-project/action.yml
+++ b/.github/actions/build-single-project/action.yml
@@ -38,6 +38,9 @@
     - name: "Install Cmake"
       shell: bash
       run: echo "yes" | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "cmake;3.22.1"
+    - name: "Install Android SDK Build-Tools"
+      shell: bash
+      run: echo "yes" | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "build-tools;34.0.0-rc3"
     - name: "Set environment variables"
       shell: bash
       run: |
diff --git a/.github/ci-control/ci-config.json b/.github/ci-control/ci-config.json
index 2d07c46..dfb2af6 100644
--- a/.github/ci-control/ci-config.json
+++ b/.github/ci-control/ci-config.json
@@ -7,11 +7,13 @@
     ],
     "compose" : {
         "include" : [
+            "compose-runtime"
         ],
         "default": false
     },
     "main" : {
         "exclude" : [
+          "compose-runtime",
           "room"
         ],
         "default": true
diff --git a/OWNERS b/OWNERS
index e6c536a..9104654 100644
--- a/OWNERS
+++ b/OWNERS
@@ -27,6 +27,7 @@
 per-file *settings.gradle = alanv@google.com, aurimas@google.com
 per-file *libraryversions.toml = jnichol@google.com
 per-file *libraryversions.toml = diegovela@google.com, akulian@google.com, kchyn@google.com
+per-file *docs-public/build.gradle = juanrdz@google.com
 
 # Copybara can self-approve CLs within synced docs.
 per-file docs/** = copybara-worker-blackhole@google.com
\ No newline at end of file
diff --git a/activity/activity-compose/integration-tests/activity-demos/src/main/AndroidManifest.xml b/activity/activity-compose/integration-tests/activity-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/activity/activity-compose/integration-tests/activity-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/activity/activity-compose/samples/src/main/AndroidManifest.xml b/activity/activity-compose/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/activity/activity-compose/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/activity/activity-compose/src/main/AndroidManifest.xml b/activity/activity-compose/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/activity/activity-compose/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/activity/activity-ktx/src/main/AndroidManifest.xml b/activity/activity-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/activity/activity-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/activity/activity/api/current.ignore b/activity/activity/api/current.ignore
index 3da4564..b9cc6d6 100644
--- a/activity/activity/api/current.ignore
+++ b/activity/activity/api/current.ignore
@@ -7,7 +7,3 @@
     Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter input in androidx.activity.result.contract.ActivityResultContract.getSynchronousResult(android.content.Context context, I input)
 InvalidNullConversion: androidx.activity.result.contract.ActivityResultContract.SynchronousResult#SynchronousResult(T) parameter #0:
     Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.activity.result.contract.ActivityResultContract.SynchronousResult(T value)
-
-
-RemovedMethod: androidx.activity.ComponentActivity#onBackPressed():
-    Removed method androidx.activity.ComponentActivity.onBackPressed()
diff --git a/activity/activity/api/restricted_current.ignore b/activity/activity/api/restricted_current.ignore
index 3da4564..b9cc6d6 100644
--- a/activity/activity/api/restricted_current.ignore
+++ b/activity/activity/api/restricted_current.ignore
@@ -7,7 +7,3 @@
     Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter input in androidx.activity.result.contract.ActivityResultContract.getSynchronousResult(android.content.Context context, I input)
 InvalidNullConversion: androidx.activity.result.contract.ActivityResultContract.SynchronousResult#SynchronousResult(T) parameter #0:
     Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.activity.result.contract.ActivityResultContract.SynchronousResult(T value)
-
-
-RemovedMethod: androidx.activity.ComponentActivity#onBackPressed():
-    Removed method androidx.activity.ComponentActivity.onBackPressed()
diff --git a/annotation/annotation-experimental/src/main/AndroidManifest.xml b/annotation/annotation-experimental/src/main/AndroidManifest.xml
deleted file mode 100644
index b303288..0000000
--- a/annotation/annotation-experimental/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<manifest />
diff --git a/appactions/interaction/integration-tests/testapp/src/main/java/androidx/appactions/interaction/testapp/MainActivity.kt b/appactions/interaction/integration-tests/testapp/src/main/java/androidx/appactions/interaction/testapp/MainActivity.kt
index 9f21059..c9fbc82 100644
--- a/appactions/interaction/integration-tests/testapp/src/main/java/androidx/appactions/interaction/testapp/MainActivity.kt
+++ b/appactions/interaction/integration-tests/testapp/src/main/java/androidx/appactions/interaction/testapp/MainActivity.kt
@@ -6,11 +6,9 @@
 import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.widget.AppCompatButton
-import java.util.Locale
-import java.util.concurrent.TimeUnit
 
 class MainActivity : AppCompatActivity() {
-  private var duration = 59
+  private var duration = 120
   private var hasRunningTimer = false
 
   override fun onCreate(savedInstanceState: Bundle?) {
@@ -28,27 +26,16 @@
         object : CountDownTimer((duration * 1000).toLong(), 1000) {
           override fun onTick(millisUntilFinished: Long) {
             runOnUiThread {
-              val time = String.format(
-                Locale.getDefault(),
-                "%02d:%02d:%02d",
-                TimeUnit.MILLISECONDS.toHours(millisUntilFinished),
-                TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(
-                  TimeUnit.MILLISECONDS.toHours(millisUntilFinished)
-                ),
-                TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(
-                  TimeUnit.MILLISECONDS.toHours(millisUntilFinished)
-                )
-              )
-              val hourMinSecArray = time.split(":")
-              hours.text = hourMinSecArray[0]
-              mins.text = hourMinSecArray[1]
-              seconds.text = hourMinSecArray[2]
+              val timeArray = millisecondsToTimeString(millisUntilFinished).split(":")
+              hours.text = timeArray[0]
+              mins.text = timeArray[1]
+              seconds.text = timeArray[2]
             }
           }
 
           override fun onFinish() {
             // Reset timer duration
-            duration = 59
+            duration = 120
             hasRunningTimer = false
           }
         }.start()
@@ -57,4 +44,11 @@
       }
     }
   }
+
+  private fun millisecondsToTimeString(milliseconds: Long): String {
+    val hours = milliseconds / 3600000
+    val minutes = (milliseconds % 3600000) / 60000
+    val seconds = (milliseconds % 60000) / 1000
+    return String.format("%02d:%02d:%02d", hours, minutes, seconds)
+  }
 }
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
index a58ae85..8fdbdc0 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
@@ -34,56 +34,15 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
 private const val CAPABILITY_NAME: String = "actions.intent.CREATE_CALL"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(CreateCall.Arguments::class.java, CreateCall.Arguments::Builder)
-        .setOutput(CreateCall.Output::class.java)
-        .bindOptionalParameter(
-            "call.callFormat",
-            { properties ->
-                Optional.ofNullable(
-                    properties[CreateCall.PropertyMapStrings.CALL_FORMAT.key]
-                        as Property<Call.CanonicalValue.CallFormat>
-                )
-            },
-            CreateCall.Arguments.Builder::setCallFormat,
-            TypeConverters.CALL_FORMAT_PARAM_VALUE_CONVERTER,
-            TypeConverters.CALL_FORMAT_ENTITY_CONVERTER
-        )
-        .bindRepeatedParameter(
-            "call.participant",
-            { properties ->
-                Optional.ofNullable(
-                    properties[CreateCall.PropertyMapStrings.PARTICIPANT.key]
-                        as Property<Participant>
-                )
-            },
-            CreateCall.Arguments.Builder::setParticipantList,
-            ParticipantValue.PARAM_VALUE_CONVERTER,
-            EntityConverter.of(PARTICIPANT_TYPE_SPEC)
-        )
-        .bindOptionalOutput(
-            "call",
-            { output -> Optional.ofNullable(output.call) },
-            ParamValueConverter.of(CALL_TYPE_SPEC)::toParamValue
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            CreateCall.ExecutionStatus::toParamValue
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.CREATE_CALL */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class CreateCall private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
         CALL_FORMAT("call.callFormat"),
-        PARTICIPANT("call.participant"),
+        PARTICIPANT("call.participant")
     }
 
     class CapabilityBuilder :
@@ -107,7 +66,7 @@
     class Arguments
     internal constructor(
         val callFormat: Call.CanonicalValue.CallFormat?,
-        val participantList: List<ParticipantValue>,
+        val participantList: List<ParticipantValue>
     ) {
         override fun toString(): String {
             return "Arguments(callFormat=$callFormat, participantList=$participantList)"
@@ -216,4 +175,42 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "call.callFormat",
+                    { properties ->
+                        properties[PropertyMapStrings.CALL_FORMAT.key]
+                            as? Property<Call.CanonicalValue.CallFormat>
+                    },
+                    Arguments.Builder::setCallFormat,
+                    TypeConverters.CALL_FORMAT_PARAM_VALUE_CONVERTER,
+                    TypeConverters.CALL_FORMAT_ENTITY_CONVERTER
+                )
+                .bindRepeatedParameter(
+                    "call.participant",
+                    { properties ->
+                        properties[PropertyMapStrings.PARTICIPANT.key] as? Property<Participant>
+                    },
+                    Arguments.Builder::setParticipantList,
+                    ParticipantValue.PARAM_VALUE_CONVERTER,
+                    EntityConverter.of(PARTICIPANT_TYPE_SPEC)
+                )
+                .bindOutput(
+                    "call",
+                    Output::call,
+                    ParamValueConverter.of(CALL_TYPE_SPEC)::toParamValue
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
index 806e5ac..b559a71 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
@@ -35,56 +35,15 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
 private const val CAPABILITY_NAME: String = "actions.intent.CREATE_MESSAGE"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(CreateMessage.Arguments::class.java, CreateMessage.Arguments::Builder)
-        .setOutput(CreateMessage.Output::class.java)
-        .bindRepeatedParameter(
-            "message.recipient",
-            { properties ->
-                Optional.ofNullable(
-                    properties[CreateMessage.PropertyMapStrings.RECIPIENT.key]
-                        as Property<Recipient>
-                )
-            },
-            CreateMessage.Arguments.Builder::setRecipientList,
-            RecipientValue.PARAM_VALUE_CONVERTER,
-            EntityConverter.of(RECIPIENT_TYPE_SPEC)
-        )
-        .bindOptionalParameter(
-            "message.text",
-            { properties ->
-                Optional.ofNullable(
-                    properties[CreateMessage.PropertyMapStrings.MESSAGE_TEXT.key]
-                        as Property<StringValue>
-                )
-            },
-            CreateMessage.Arguments.Builder::setMessageText,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER
-        )
-        .bindOptionalOutput(
-            "message",
-            { output -> Optional.ofNullable(output.message) },
-            ParamValueConverter.of(MESSAGE_TYPE_SPEC)::toParamValue
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            CreateMessage.ExecutionStatus::toParamValue
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.CREATE_MESSAGE */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class CreateMessage private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
         MESSAGE_TEXT("message.text"),
-        RECIPIENT("message.recipient"),
+        RECIPIENT("message.recipient")
     }
 
     class CapabilityBuilder :
@@ -218,4 +177,41 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindRepeatedParameter(
+                    "message.recipient",
+                    { properties ->
+                        properties[PropertyMapStrings.RECIPIENT.key] as? Property<Recipient>
+                    },
+                    Arguments.Builder::setRecipientList,
+                    RecipientValue.PARAM_VALUE_CONVERTER,
+                    EntityConverter.of(RECIPIENT_TYPE_SPEC)
+                )
+                .bindParameter(
+                    "message.text",
+                    { properties ->
+                        properties[PropertyMapStrings.MESSAGE_TEXT.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setMessageText,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "message",
+                    Output::message,
+                    ParamValueConverter.of(MESSAGE_TYPE_SPEC)::toParamValue
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/api/api_lint.ignore b/appactions/interaction/interaction-capabilities-core/api/api_lint.ignore
deleted file mode 100644
index af5cd99..0000000
--- a/appactions/interaction/interaction-capabilities-core/api/api_lint.ignore
+++ /dev/null
@@ -1,15 +0,0 @@
-// Baseline format: 1.0
-BuilderSetStyle: androidx.appactions.interaction.capabilities.core.Capability.Builder#asBuilder():
-    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method androidx.appactions.interaction.capabilities.core.Capability.Builder.asBuilder()
-
-
-MissingGetterMatchingBuilder: androidx.appactions.interaction.capabilities.core.Capability.Builder#setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT>):
-    androidx.appactions.interaction.capabilities.core.Capability does not declare a `getExecutionCallback()` method matching method androidx.appactions.interaction.capabilities.core.Capability.Builder.setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT>)
-MissingGetterMatchingBuilder: androidx.appactions.interaction.capabilities.core.Capability.Builder#setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync<ArgumentsT,OutputT>):
-    androidx.appactions.interaction.capabilities.core.Capability does not declare a `getExecutionCallback()` method matching method androidx.appactions.interaction.capabilities.core.Capability.Builder.setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync<ArgumentsT,OutputT>)
-MissingGetterMatchingBuilder: androidx.appactions.interaction.capabilities.core.Capability.Builder#setExecutionSessionFactory(kotlin.jvm.functions.Function1<? super androidx.appactions.interaction.capabilities.core.HostProperties,? extends ExecutionSessionT>):
-    androidx.appactions.interaction.capabilities.core.Capability does not declare a `getExecutionSessionFactory()` method matching method androidx.appactions.interaction.capabilities.core.Capability.Builder.setExecutionSessionFactory(kotlin.jvm.functions.Function1<? super androidx.appactions.interaction.capabilities.core.HostProperties,? extends ExecutionSessionT>)
-
-
-StaticFinalBuilder: androidx.appactions.interaction.capabilities.core.Capability.Builder:
-    Builder must be final: androidx.appactions.interaction.capabilities.core.Capability.Builder
diff --git a/appactions/interaction/interaction-capabilities-core/api/current.txt b/appactions/interaction/interaction-capabilities-core/api/current.txt
index b2e08cf..4045e23 100644
--- a/appactions/interaction/interaction-capabilities-core/api/current.txt
+++ b/appactions/interaction/interaction-capabilities-core/api/current.txt
@@ -14,7 +14,6 @@
   }
 
   public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
-    method public final BuilderT asBuilder();
     method public androidx.appactions.interaction.capabilities.core.Capability build();
     method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT> executionCallback);
     method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync<ArgumentsT,OutputT> executionCallbackAsync);
diff --git a/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt b/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt
index b2e08cf..4045e23 100644
--- a/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt
+++ b/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt
@@ -14,7 +14,6 @@
   }
 
   public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
-    method public final BuilderT asBuilder();
     method public androidx.appactions.interaction.capabilities.core.Capability build();
     method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT> executionCallback);
     method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync<ArgumentsT,OutputT> executionCallbackAsync);
diff --git a/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt b/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt
index b2e08cf..4045e23 100644
--- a/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt
+++ b/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt
@@ -14,7 +14,6 @@
   }
 
   public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
-    method public final BuilderT asBuilder();
     method public androidx.appactions.interaction.capabilities.core.Capability build();
     method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT> executionCallback);
     method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync<ArgumentsT,OutputT> executionCallbackAsync);
diff --git a/appactions/interaction/interaction-capabilities-core/build.gradle b/appactions/interaction/interaction-capabilities-core/build.gradle
index 6683d10..5bca53c 100644
--- a/appactions/interaction/interaction-capabilities-core/build.gradle
+++ b/appactions/interaction/interaction-capabilities-core/build.gradle
@@ -25,8 +25,6 @@
 dependencies {
     api(project(path: ":appactions:interaction:interaction-proto", configuration: "shadowJar"))
 
-    annotationProcessor(libs.autoValue)
-
     api(libs.autoValueAnnotations)
     implementation(libs.guavaListenableFuture)
     implementation(libs.kotlinCoroutinesCore)
@@ -34,7 +32,6 @@
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
     implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
 
-    testAnnotationProcessor(libs.autoValue)
     testImplementation(project(":appactions:interaction:interaction-capabilities-testing"))
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt
index 6fc55f2..a720034 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt
@@ -16,6 +16,7 @@
 
 package androidx.appactions.interaction.capabilities.core
 
+import android.annotation.SuppressLint
 import androidx.annotation.RestrictTo
 import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.SingleTurnCapabilityImpl
@@ -58,6 +59,7 @@
     /**
      * An abstract Builder class for Capability.
      */
+    @SuppressLint("StaticFinalBuilder")
     abstract class Builder<
         BuilderT :
         Builder<
@@ -76,7 +78,7 @@
         private var property: Map<String, Property<*>>? = null
         private var executionCallback: ExecutionCallback<ArgumentsT, OutputT>? = null
         private var sessionFactory:
-                (hostProperties: HostProperties?) -> ExecutionSessionT? = { _ -> null }
+            (hostProperties: HostProperties?) -> ExecutionSessionT? = { _ -> null }
         private var actionSpec: ActionSpec<ArgumentsT, OutputT>? = null
 
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -91,10 +93,14 @@
          * @suppress
          */
         @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        protected open val sessionBridge: SessionBridge<ExecutionSessionT, ConfirmationT>? = null
+        protected open val sessionBridge: SessionBridge<
+            ExecutionSessionT,
+            ArgumentsT,
+            ConfirmationT
+        >? = null
 
         @Suppress("UNCHECKED_CAST")
-        fun asBuilder(): BuilderT {
+        private fun asBuilder(): BuilderT {
             return this as BuilderT
         }
 
@@ -123,6 +129,7 @@
          * This method accepts a coroutine-based ExecutionCallback instance. There is also an
          * overload which accepts the ExecutionCallbackAsync instead.
          */
+        @SuppressLint("MissingGetterMatchingBuilder")
         fun setExecutionCallback(executionCallback: ExecutionCallback<ArgumentsT, OutputT>) =
             asBuilder().apply {
                 this.executionCallback = executionCallback
@@ -137,6 +144,7 @@
          * This method accepts the ExecutionCallbackAsync interface which returns a
          * [ListenableFuture].
          */
+        @SuppressLint("MissingGetterMatchingBuilder")
         fun setExecutionCallback(
             executionCallbackAsync: ExecutionCallbackAsync<ArgumentsT, OutputT>
         ) = asBuilder().apply {
@@ -144,12 +152,13 @@
         }
 
         /**
-         * Sets the lambda used to create [ExecutionSession] instances for this
+         * Sets the lambda used to create [ExecutionSessionT] instances for this
          * capability.
          *
          * [setExecutionSessionFactory] and [setExecutionCallback] are mutually exclusive, so
          * calling one will nullify the other.
          */
+        @SuppressLint("MissingGetterMatchingBuilder")
         open fun setExecutionSessionFactory(
             sessionFactory: (hostProperties: HostProperties?) -> ExecutionSessionT
         ): BuilderT = asBuilder().apply {
@@ -168,15 +177,11 @@
                     executionCallback!!
                 )
             } else {
-                val checkedSessionFactory = requireNotNull(sessionFactory) {
-                    "either setExecutionCallback or setExecutionSessionFactory" +
-                        " must be called before build"
-                }
                 return TaskCapabilityImpl(
                     checkedId,
                     actionSpec!!,
                     checkedProperty,
-                    checkedSessionFactory,
+                    sessionFactory,
                     sessionBridge!!,
                     ::EmptyTaskUpdater
                 )
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ArgumentsWrapper.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ArgumentsWrapper.kt
index 3eece1d..8aaadae 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ArgumentsWrapper.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ArgumentsWrapper.kt
@@ -54,7 +54,7 @@
             ) {
                 null
             } else {
-                RequestMetadata.newBuilder().setRequestType(fulfillment.type).build()
+                RequestMetadata(fulfillment.type)
             }
         }
 
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java
index d983859..0600dbb 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java
@@ -27,10 +27,6 @@
     UNCHANGED_DISAMBIG_STATE(3),
     INVALID_RESOLVER(4),
     STRUCT_CONVERSION_FAILURE(5),
-    // TODO(b/276354491): remove SYNC / CONFIRM / TOUCH_EVENT failure codes
-    SYNC_REQUEST_FAILURE(6),
-    CONFIRMATION_REQUEST_FAILURE(7),
-    TOUCH_EVENT_REQUEST_FAILURE(8),
     EXTERNAL_EXCEPTION(9),
     SESSION_ALREADY_DESTROYED(10);
 
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/RequestMetadata.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/RequestMetadata.java
deleted file mode 100644
index 868a7d8..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/RequestMetadata.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment;
-
-import com.google.auto.value.AutoValue;
-
-/** Represents metadata from the Assistant FulfillmentRequest. */
-@AutoValue
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class RequestMetadata {
-
-    /** Create a Builder instance for building a RequestMetadata instance. */
-    @NonNull
-    public static Builder newBuilder() {
-        return new AutoValue_RequestMetadata.Builder();
-    }
-
-    /** The Type of request Assistant is sending on this FulfillmentRequest. */
-    @NonNull
-    public abstract Fulfillment.Type requestType();
-
-    /** Builder for RequestMetadata. */
-    @AutoValue.Builder
-    public abstract static class Builder {
-        /** Sets the FulfillmentRequest.Type. */
-        @NonNull
-        public abstract Builder setRequestType(@NonNull Fulfillment.Type requestType);
-
-        /** Builds the RequestMetadata instance. */
-        @NonNull
-        public abstract RequestMetadata build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/RequestMetadata.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/RequestMetadata.kt
new file mode 100644
index 0000000..37be7ca
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/RequestMetadata.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+data class RequestMetadata internal constructor(
+    val requestType: Fulfillment.Type
+) {
+    companion object {
+        @JvmStatic
+        fun create(requestType: Fulfillment.Type): RequestMetadata {
+            return RequestMetadata(requestType)
+        }
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
index 32f3753..f61a922 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
@@ -20,10 +20,10 @@
 import androidx.appactions.interaction.capabilities.core.ExecutionCallback
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
+import androidx.appactions.interaction.capabilities.core.impl.utils.CapabilityLogger
+import androidx.appactions.interaction.capabilities.core.impl.utils.LoggerInternal
 import androidx.appactions.interaction.capabilities.core.impl.utils.invokeExternalSuspendBlock
-import androidx.appactions.interaction.capabilities.core.impl.utils.isCausedBy
-import androidx.appactions.interaction.capabilities.core.impl.utils.toErrorStatusInternal
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.InvalidRequestException
+import androidx.appactions.interaction.capabilities.core.impl.utils.handleExceptionFromRequestProcessing
 import androidx.appactions.interaction.proto.AppActionsContext.AppDialogState
 import androidx.appactions.interaction.proto.FulfillmentResponse
 import androidx.appactions.interaction.proto.ParamValue
@@ -84,11 +84,12 @@
                 }
                 callback.onSuccess(convertToFulfillmentResponse(output))
             } catch (t: Throwable) {
-                callback.onError(t.toErrorStatusInternal())
-                // if the exception is caused by a bad request, do not crash the app
-                if (!t.isCausedBy(InvalidRequestException::class)) {
-                    throw t
-                }
+                LoggerInternal.log(
+                    CapabilityLogger.LogLevel.ERROR,
+                    LOG_TAG,
+                    "single-turn capability execution failed."
+                )
+                handleExceptionFromRequestProcessing(t, callback::onError)
             } finally {
                 UiHandleRegistry.unregisterUiHandle(uiHandle)
                 mutex.unlock(owner = this@SingleTurnCapabilitySession)
@@ -109,4 +110,8 @@
         }
         return fulfillmentResponseBuilder.build()
     }
+
+    companion object {
+        private const val LOG_TAG = "SingleTurnCapability"
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/CheckedInterfaces.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/CheckedInterfaces.java
index 056c6e8..eba3f11 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/CheckedInterfaces.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/CheckedInterfaces.java
@@ -37,7 +37,7 @@
      * @param <R>
      */
     @FunctionalInterface
-    public interface Function<T, R> {
+    interface Function<T, R> {
         R apply(T t) throws StructConversionException;
     }
 
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/FieldBinding.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/FieldBinding.java
deleted file mode 100644
index 2e2f22a..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/FieldBinding.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.converters;
-
-import androidx.appactions.interaction.protobuf.Value;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.Optional;
-import java.util.function.Function;
-
-@AutoValue
-abstract class FieldBinding<T, BuilderT> {
-
-    static <T, BuilderT> FieldBinding<T, BuilderT> create(
-            String name,
-            Function<T, Optional<Value>> valueGetter,
-            CheckedInterfaces.BiConsumer<BuilderT, Optional<Value>> valueSetter) {
-        return new AutoValue_FieldBinding<>(name, valueGetter, valueSetter);
-    }
-
-    abstract String name();
-
-    abstract Function<T, Optional<Value>> valueGetter();
-
-    abstract CheckedInterfaces.BiConsumer<BuilderT, Optional<Value>> valueSetter();
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/FieldBinding.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/FieldBinding.kt
new file mode 100644
index 0000000..9ca198c
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/FieldBinding.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.converters
+
+import androidx.appactions.interaction.protobuf.Value
+import java.util.function.Function
+
+internal data class FieldBinding<T, BuilderT> constructor(
+    val name: String,
+    val valueGetter: Function<T, Value?>,
+    val valueSetter: CheckedInterfaces.BiConsumer<BuilderT, Value>
+) {
+    companion object {
+        @JvmStatic
+        fun <T, BuilderT> create(
+            name: String,
+            valueGetter: Function<T, Value?>,
+            valueSetter: CheckedInterfaces.BiConsumer<BuilderT, Value>
+        ): FieldBinding<T, BuilderT> {
+            return FieldBinding(name, valueGetter, valueSetter)
+        }
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
index efacbfb4..a963bf6 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
@@ -18,9 +18,13 @@
 
 import androidx.annotation.NonNull;
 import androidx.appactions.builtintypes.experimental.properties.Attendee;
+import androidx.appactions.builtintypes.experimental.properties.EndDate;
 import androidx.appactions.builtintypes.experimental.properties.ItemListElement;
+import androidx.appactions.builtintypes.experimental.properties.Name;
 import androidx.appactions.builtintypes.experimental.properties.Participant;
 import androidx.appactions.builtintypes.experimental.properties.Recipient;
+import androidx.appactions.builtintypes.experimental.properties.StartDate;
+import androidx.appactions.builtintypes.experimental.properties.Text;
 import androidx.appactions.builtintypes.experimental.types.Alarm;
 import androidx.appactions.builtintypes.experimental.types.CalendarEvent;
 import androidx.appactions.builtintypes.experimental.types.Call;
@@ -73,20 +77,17 @@
                     .build();
 
     public static final TypeSpec<Person> PERSON_TYPE_SPEC =
-            TypeSpecBuilder.newBuilderForThing(
-                            "Person",
-                            Person::Builder,
-                            Person.Builder::build)
-                    .bindStringField("email",
-                            person -> Optional.ofNullable(person.getEmail()),
-                            Person.Builder::setEmail)
+            TypeSpecBuilder.newBuilderForThing("Person", Person::Builder, Person.Builder::build)
+                    .bindStringField("email", Person::getEmail, Person.Builder::setEmail)
                     .bindStringField(
-                            "telephone",
-                            person -> Optional.ofNullable(person.getTelephone()),
-                            Person.Builder::setTelephone)
-                    .bindStringField("name",
-                            person -> Optional.ofNullable(person.getName())
-                                    .flatMap(name -> Optional.ofNullable(name.asText())),
+                            "telephone", Person::getTelephone, Person.Builder::setTelephone)
+                    .bindStringField(
+                            "name",
+                            person ->
+                                    Optional.ofNullable(person)
+                                            .map(Person::getName)
+                                            .map(Name::asText)
+                                            .orElse(null),
                             Person.Builder::setName)
                     .build();
     public static final TypeSpec<Alarm> ALARM_TYPE_SPEC =
@@ -109,13 +110,19 @@
                             CalendarEvent.Builder::build)
                     .bindZonedDateTimeField(
                             "startDate",
-                            calendarEvent -> Optional.ofNullable(
-                                    calendarEvent.getStartDate().asZonedDateTime()),
+                            calendarEvent ->
+                                    Optional.ofNullable(calendarEvent)
+                                            .map(CalendarEvent::getStartDate)
+                                            .map(StartDate::asZonedDateTime)
+                                            .orElse(null),
                             CalendarEvent.Builder::setStartDate)
                     .bindZonedDateTimeField(
                             "endDate",
-                            calendarEvent -> Optional.ofNullable(
-                                    calendarEvent.getEndDate().asZonedDateTime()),
+                            calendarEvent ->
+                                    Optional.ofNullable(calendarEvent)
+                                            .map(CalendarEvent::getEndDate)
+                                            .map(EndDate::asZonedDateTime)
+                                            .orElse(null),
                             CalendarEvent.Builder::setEndDate)
                     .bindRepeatedSpecField(
                             "attendee",
@@ -130,11 +137,11 @@
                             SafetyCheck.Builder::build)
                     .bindDurationField(
                             "duration",
-                            safetyCheck -> Optional.ofNullable(safetyCheck.getDuration()),
+                            SafetyCheck::getDuration,
                             SafetyCheck.Builder::setDuration)
                     .bindZonedDateTimeField(
                             "checkInTime",
-                            safetyCheck -> Optional.ofNullable(safetyCheck.getCheckInTime()),
+                            SafetyCheck::getCheckInTime,
                             SafetyCheck.Builder::setCheckInTime)
                     .build();
     public static final TypeSpec<Recipient> RECIPIENT_TYPE_SPEC =
@@ -152,11 +159,8 @@
                             PERSON_TYPE_SPEC)
                     .build();
     public static final TypeSpec<Message> MESSAGE_TYPE_SPEC =
-            TypeSpecBuilder.newBuilderForThing(
-                            "Message",
-                            Message::Builder,
-                            Message.Builder::build)
-                    .bindIdentifier(message -> Optional.ofNullable(message.getIdentifier()))
+            TypeSpecBuilder.newBuilderForThing("Message", Message::Builder, Message.Builder::build)
+                    .bindIdentifier(Message::getIdentifier)
                     .bindRepeatedSpecField(
                             "recipient",
                             Message::getRecipientList,
@@ -164,7 +168,11 @@
                             RECIPIENT_TYPE_SPEC)
                     .bindStringField(
                             "text",
-                            message -> Optional.of(message.getText().asText()),
+                            message ->
+                                    Optional.ofNullable(message)
+                                            .map(Message::getText)
+                                            .map(Text::asText)
+                                            .orElse(null),
                             Message.Builder::setText)
                     .build();
     public static final TypeSpec<Call> CALL_TYPE_SPEC =
@@ -172,7 +180,7 @@
                             "Call",
                             Call::Builder,
                             Call.Builder::build)
-                    .bindIdentifier(call -> Optional.ofNullable(call.getIdentifier()))
+                    .bindIdentifier(Call::getIdentifier)
                     .bindRepeatedSpecField(
                             "participant",
                             Call::getParticipantList,
@@ -368,11 +376,11 @@
                         SearchAction.Builder<T>::build)
                 .bindStringField(
                         "query",
-                        (searchAction) -> Optional.ofNullable(searchAction.getQuery()),
+                        SearchAction::getQuery,
                         SearchAction.Builder<T>::setQuery)
                 .bindSpecField(
                         "filter",
-                        (searchAction) -> Optional.ofNullable(searchAction.getFilter()),
+                        SearchAction::getFilter,
                         SearchAction.Builder<T>::setFilter,
                         nestedTypeSpec)
                 .build();
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java
index 5222d8a..21c30a6 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java
@@ -19,20 +19,18 @@
 import static androidx.appactions.interaction.capabilities.core.impl.utils.ImmutableCollectors.toImmutableList;
 
 import androidx.appactions.builtintypes.experimental.types.Thing;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
 import androidx.appactions.interaction.protobuf.ListValue;
 import androidx.appactions.interaction.protobuf.Struct;
 import androidx.appactions.interaction.protobuf.Value;
 
 import java.time.Duration;
-import java.time.OffsetDateTime;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Optional;
+import java.util.Objects;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -43,7 +41,7 @@
     private final Supplier<BuilderT> mBuilderSupplier;
     private final Function<BuilderT, T> mBuilderFinalizer;
     private CheckedInterfaces.Consumer<Struct> mStructValidator;
-    private Function<T, Optional<String>> mIdentifierGetter = (unused) -> Optional.empty();
+    private Function<T, String> mIdentifierGetter = (unused) -> null;
 
     private TypeSpecBuilder(
             String typeName,
@@ -51,7 +49,7 @@
             Function<BuilderT, T> builderFinalizer) {
         this.mBuilderSupplier = builderSupplier;
         this.mBuilderFinalizer = builderFinalizer;
-        this.bindStringField("@type", (unused) -> Optional.of(typeName), (builder, val) -> {})
+        this.bindStringField("@type", (unused) -> typeName, (builder, val) -> {})
                 .setStructValidator(
                         struct -> {
                             if (!getFieldFromStruct(struct, "@type")
@@ -92,11 +90,6 @@
         }
     }
 
-    static <T, BuilderT extends BuilderOf<T>> TypeSpecBuilder<T, BuilderT> newBuilder(
-            String typeName, Supplier<BuilderT> builderSupplier) {
-        return new TypeSpecBuilder<>(typeName, builderSupplier, BuilderT::build);
-    }
-
     static <T, BuilderT> TypeSpecBuilder<T, BuilderT> newBuilder(
             String typeName,
             Supplier<BuilderT> builderSupplier,
@@ -115,16 +108,16 @@
                     Supplier<BuilderT> builderSupplier,
                     Function<BuilderT, T> builderFinalizer) {
         return new TypeSpecBuilder<>(typeName, builderSupplier, builderFinalizer)
-                .bindIdentifier(thing -> Optional.ofNullable(thing.getIdentifier()))
-                .bindStringField(
-                        "identifier",
-                        thing -> Optional.ofNullable(thing.getIdentifier()),
-                        BuilderT::setIdentifier)
+                .bindIdentifier(Thing::getIdentifier)
+                .bindStringField("identifier", Thing::getIdentifier, BuilderT::setIdentifier)
                 .bindStringField(
                         "name",
-                        thing ->
-                                Optional.ofNullable(thing.getName())
-                                        .flatMap(name -> Optional.ofNullable(name.asText())),
+                        thing -> {
+                            if (thing.getName() == null) {
+                                return null;
+                            }
+                            return thing.getName().asText();
+                        },
                         BuilderT::setName);
     }
 
@@ -134,15 +127,15 @@
         return this;
     }
 
-    TypeSpecBuilder<T, BuilderT> bindIdentifier(Function<T, Optional<String>> identifierGetter) {
+    TypeSpecBuilder<T, BuilderT> bindIdentifier(Function<T, String> identifierGetter) {
         this.mIdentifierGetter = identifierGetter;
         return this;
     }
 
     private TypeSpecBuilder<T, BuilderT> bindFieldInternal(
             String name,
-            Function<T, Optional<Value>> valueGetter,
-            CheckedInterfaces.BiConsumer<BuilderT, Optional<Value>> valueSetter) {
+            Function<T, Value> valueGetter,
+            CheckedInterfaces.BiConsumer<BuilderT, Value> valueSetter) {
         mBindings.add(FieldBinding.create(name, valueGetter, valueSetter));
         return this;
     }
@@ -151,30 +144,28 @@
             String name,
             Function<T, List<V>> valueGetter,
             BiConsumer<BuilderT, List<V>> valueSetter,
-            Function<V, Optional<Value>> toValue,
+            Function<V, Value> toValue,
             CheckedInterfaces.Function<Value, V> fromValue) {
         return bindFieldInternal(
                 name,
                 /** valueGetter= */
                 object -> {
-                    if (valueGetter.apply(object).isEmpty()) {
-                        return Optional.empty();
+                    List<V> valueList = valueGetter.apply(object);
+                    if (valueList == null) {
+                        return null;
                     }
-                    return Optional.of(
-                            getListValue(
-                                    valueGetter.apply(object).stream()
-                                            .map(toValue)
-                                            .filter(Optional::isPresent)
-                                            .map(Optional::get)
-                                            .collect(toImmutableList())));
+                    return getListValue(
+                            valueList.stream()
+                                    .map(toValue)
+                                    .filter(Objects::nonNull)
+                                    .collect(toImmutableList()));
                 },
                 /** valueSetter= */
                 (builder, repeatedValue) -> {
-                    List<Value> values =
-                            repeatedValue
-                                    .map(Value::getListValue)
-                                    .map(ListValue::getValuesList)
-                                    .orElseGet(Collections::emptyList);
+                    if (repeatedValue.getListValue() == null) {
+                        return;
+                    }
+                    List<Value> values = repeatedValue.getListValue().getValuesList();
                     List<V> convertedValues = new ArrayList<>();
                     for (Value value : values) {
                         convertedValues.add(fromValue.apply(value));
@@ -186,15 +177,22 @@
     /** binds a String field to read from / write to Struct */
     TypeSpecBuilder<T, BuilderT> bindStringField(
             String name,
-            Function<T, Optional<String>> stringGetter,
+            Function<T, String> stringGetter,
             BiConsumer<BuilderT, String> stringSetter) {
         return bindFieldInternal(
                 name,
-                (object) -> stringGetter.apply(object).map(TypeSpecBuilder::getStringValue),
-                (builder, value) ->
-                        value.map(Value::getStringValue)
-                                .ifPresent(
-                                        stringValue -> stringSetter.accept(builder, stringValue)));
+                (object) -> {
+                    String value = stringGetter.apply(object);
+                    if (value == null) {
+                        return null;
+                    }
+                    return TypeSpecBuilder.getStringValue(value);
+                },
+                (builder, value) -> {
+                    if (value.hasStringValue()) {
+                        stringSetter.accept(builder, value.getStringValue());
+                    }
+                });
     }
 
     /**
@@ -203,19 +201,21 @@
      */
     <E extends Enum<E>> TypeSpecBuilder<T, BuilderT> bindEnumField(
             String name,
-            Function<T, Optional<E>> valueGetter,
+            Function<T, E> valueGetter,
             BiConsumer<BuilderT, E> valueSetter,
             Class<E> enumClass) {
         return bindFieldInternal(
                 name,
-                (object) ->
-                        valueGetter
-                                .apply(object)
-                                .map(Enum::toString)
-                                .map(TypeSpecBuilder::getStringValue),
+                (object) -> {
+                    E enumVal = valueGetter.apply(object);
+                    if (enumVal == null) {
+                        return null;
+                    }
+                    return TypeSpecBuilder.getStringValue(enumVal.toString());
+                },
                 (builder, value) -> {
-                    if (value.isPresent()) {
-                        String stringValue = value.get().getStringValue();
+                    if (value.hasStringValue()) {
+                        String stringValue = value.getStringValue();
                         E[] enumValues = enumClass.getEnumConstants();
                         if (enumValues != null) {
                             for (E enumValue : enumValues) {
@@ -237,24 +237,24 @@
      */
     TypeSpecBuilder<T, BuilderT> bindDurationField(
             String name,
-            Function<T, Optional<Duration>> valueGetter,
+            Function<T, Duration> valueGetter,
             BiConsumer<BuilderT, Duration> valueSetter) {
         return bindFieldInternal(
                 name,
-                (object) ->
-                        valueGetter
-                                .apply(object)
-                                .map(Duration::toString)
-                                .map(TypeSpecBuilder::getStringValue),
+                (object) -> {
+                    Duration duration = valueGetter.apply(object);
+                    if (duration == null) {
+                        return null;
+                    }
+                    return TypeSpecBuilder.getStringValue(duration.toString());
+                },
                 (builder, value) -> {
-                    if (value.isPresent()) {
-                        try {
-                            valueSetter.accept(
-                                    builder, Duration.parse(value.get().getStringValue()));
-                        } catch (DateTimeParseException e) {
-                            throw new StructConversionException(
-                                    "Failed to parse ISO 8601 string to Duration", e);
-                        }
+                    try {
+                        valueSetter.accept(
+                                builder, Duration.parse(value.getStringValue()));
+                    } catch (DateTimeParseException e) {
+                        throw new StructConversionException(
+                                "Failed to parse ISO 8601 string to Duration", e);
                     }
                 });
     }
@@ -265,21 +265,23 @@
      */
     TypeSpecBuilder<T, BuilderT> bindZonedDateTimeField(
             String name,
-            Function<T, Optional<ZonedDateTime>> valueGetter,
+            Function<T, ZonedDateTime> valueGetter,
             BiConsumer<BuilderT, ZonedDateTime> valueSetter) {
         return bindFieldInternal(
                 name,
-                (object) ->
-                        valueGetter
-                                .apply(object)
-                                .map(ZonedDateTime::toOffsetDateTime)
-                                .map(OffsetDateTime::toString)
-                                .map(TypeSpecBuilder::getStringValue),
+                (object) -> {
+                    ZonedDateTime zonedDateTime = valueGetter.apply(object);
+                    if (zonedDateTime == null) {
+                        return null;
+                    }
+                    return TypeSpecBuilder.getStringValue(
+                            zonedDateTime.toOffsetDateTime().toString());
+                },
                 (builder, value) -> {
-                    if (value.isPresent()) {
+                    if (value.hasStringValue()) {
                         try {
                             valueSetter.accept(
-                                    builder, ZonedDateTime.parse(value.get().getStringValue()));
+                                    builder, ZonedDateTime.parse(value.getStringValue()));
                         } catch (DateTimeParseException e) {
                             throw new StructConversionException(
                                     "Failed to parse ISO 8601 string to ZonedDateTime", e);
@@ -291,23 +293,20 @@
     /** Binds a spec field to read from / write to Struct. */
     <V> TypeSpecBuilder<T, BuilderT> bindSpecField(
             String name,
-            Function<T, Optional<V>> valueGetter,
+            Function<T, V> valueGetter,
             BiConsumer<BuilderT, V> valueSetter,
             TypeSpec<V> spec) {
         return bindFieldInternal(
                 name,
-                (object) ->
-                        valueGetter
-                                .apply(object)
-                                .map(Function.identity()) // Static analyzer incorrectly
-                                // throws error stating that the
-                                // input to toStruct is nullable. This is a workaround to avoid
-                                // the error from the analyzer.
-                                .map(spec::toValue),
-                (builder, value) -> {
-                    if (value.isPresent()) {
-                        valueSetter.accept(builder, spec.fromValue(value.get()));
+                (object) -> {
+                    V value = valueGetter.apply(object);
+                    if (value == null) {
+                        return null;
                     }
+                    return spec.toValue(value);
+                },
+                (builder, value) -> {
+                    valueSetter.accept(builder, spec.fromValue(value));
                 });
     }
 
@@ -321,16 +320,16 @@
                 name,
                 valueGetter,
                 valueSetter,
-                (element) -> Optional.ofNullable(element).map(spec::toValue),
+                spec::toValue,
                 (value) -> spec.fromValue(value));
     }
 
     TypeSpec<T> build() {
-        return new TypeSpecImpl<T, BuilderT>(
+        return new TypeSpecImpl<>(
                 mIdentifierGetter,
                 mBindings,
                 mBuilderSupplier,
                 mBuilderFinalizer,
-                Optional.ofNullable(mStructValidator));
+                mStructValidator);
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java
index 48f5a0c..da4488c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java
@@ -25,7 +25,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
@@ -35,26 +34,31 @@
  */
 final class TypeSpecImpl<T, BuilderT> implements TypeSpec<T> {
     /* The function to retrieve the identifier. */
-    final Function<T, Optional<String>> mIdentifierGetter;
+    @NonNull
+    final Function<T, String> mIdentifierGetter;
 
     /** The list of FieldBinding objects. */
+    @NonNull
     final List<FieldBinding<T, BuilderT>> mBindings;
 
     /** Validates the Struct during conversion to java object. */
-    final Optional<CheckedInterfaces.Consumer<Struct>> mStructValidator;
+    @Nullable
+    final CheckedInterfaces.Consumer<Struct> mStructValidator;
 
     /** Supplies BuilderT instances. */
+    @NonNull
     final Supplier<BuilderT> mBuilderSupplier;
 
     /** Builds the object instance. */
+    @NonNull
     final Function<BuilderT, T> mBuilderFinalizer;
 
     TypeSpecImpl(
-            Function<T, Optional<String>> identifierGetter,
-            List<FieldBinding<T, BuilderT>> bindings,
-            Supplier<BuilderT> builderSupplier,
-            Function<BuilderT, T> builderFinalizer,
-            Optional<CheckedInterfaces.Consumer<Struct>> structValidator) {
+            @NonNull Function<T, String> identifierGetter,
+            @NonNull List<FieldBinding<T, BuilderT>> bindings,
+            @NonNull Supplier<BuilderT> builderSupplier,
+            @NonNull Function<BuilderT, T> builderFinalizer,
+            @Nullable CheckedInterfaces.Consumer<Struct> structValidator) {
         this.mIdentifierGetter = identifierGetter;
         this.mBindings = Collections.unmodifiableList(bindings);
         this.mBuilderSupplier = builderSupplier;
@@ -65,7 +69,7 @@
     @Nullable
     @Override
     public String getIdentifier(T obj) {
-        return mIdentifierGetter.apply(obj).orElse(null);
+        return mIdentifierGetter.apply(obj);
     }
 
     /** Converts a java object into a Struct proto using List of FieldBinding. */
@@ -74,9 +78,10 @@
     public Value toValue(@NonNull T obj) {
         Struct.Builder structBuilder = Struct.newBuilder();
         for (FieldBinding<T, BuilderT> binding : mBindings) {
-            binding.valueGetter()
-                    .apply(obj)
-                    .ifPresent(value -> structBuilder.putFields(binding.name(), value));
+            Value value = binding.getValueGetter().apply(obj);
+            if (value != null) {
+                structBuilder.putFields(binding.getName(), value);
+            }
         }
         return Value.newBuilder().setStructValue(structBuilder).build();
     }
@@ -94,15 +99,17 @@
             throw new StructConversionException(
                     String.format("TypeSpecImpl cannot deserializes non-Struct value: %s", value));
         }
-        if (mStructValidator.isPresent()) {
-            mStructValidator.get().accept(struct);
+        if (mStructValidator != null) {
+            mStructValidator.accept(struct);
         }
 
         BuilderT builder = mBuilderSupplier.get();
         Map<String, Value> fieldsMap = struct.getFieldsMap();
         for (FieldBinding<T, BuilderT> binding : mBindings) {
-            Optional<Value> fieldValue = Optional.ofNullable(fieldsMap.get(binding.name()));
-            binding.valueSetter().accept(builder, fieldValue);
+            Value fieldValue = fieldsMap.get(binding.getName());
+            if (fieldValue != null) {
+                binding.getValueSetter().accept(builder, fieldValue);
+            }
         }
         return mBuilderFinalizer.apply(builder);
     }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt
index b060fc8..557cc1c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.impl.exceptions
 
 /** Represents exceptions that happen as a result of some incoming request that is invalid. */
-sealed class InvalidRequestException : Exception {
+open class InvalidRequestException : Exception {
     constructor(message: String?) : super(message)
     constructor(message: String?, cause: Throwable?) : super(message, cause)
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java
deleted file mode 100644
index eb8d0ad..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.spec;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.proto.AppActionsContext.AppAction;
-import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * A specification for an action, describing it from the app's point of view.
- *
- * @param <ArgumentsT> typed representation of action's arguments.
- * @param <OutputT>    typed action's execution output.
- */
-public interface ActionSpec<ArgumentsT, OutputT> {
-
-    /** Converts the property to the {@code AppAction} proto. */
-    @NonNull
-    AppAction convertPropertyToProto(@NonNull Map<String,
-            Property<?>> property);
-
-    /** Builds this action's arguments from an ArgumentsWrapper instance. */
-    @NonNull
-    ArgumentsT buildArguments(@NonNull Map<String, List<ParamValue>> args)
-            throws StructConversionException;
-
-    /** Converts the output to the {@code StructuredOutput} proto. */
-    @NonNull
-    StructuredOutput convertOutputToProto(OutputT output);
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.kt
new file mode 100644
index 0000000..450d3d3
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.spec
+
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.proto.AppActionsContext
+import androidx.appactions.interaction.proto.FulfillmentResponse
+import androidx.appactions.interaction.proto.ParamValue
+
+/**
+ * A specification for an action, describing it from the app's point of view.
+ *
+ * @param ArgumentsT typed representation of action's arguments.
+ * @param OutputT    typed action's execution output.
+ */
+interface ActionSpec<ArgumentsT, OutputT> {
+
+    /** Converts the property to the `AppAction` proto.  */
+    fun convertPropertyToProto(property: Map<String, Property<*>>): AppActionsContext.AppAction
+
+    /** Builds this action's arguments from an ArgumentsWrapper instance.  */
+    @Throws(StructConversionException::class)
+    fun buildArguments(args: Map<String, List<ParamValue>>): ArgumentsT
+
+    /** Converts the output to the `StructuredOutput` proto.  */
+    fun convertOutputToProto(output: OutputT): FulfillmentResponse.StructuredOutput
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
deleted file mode 100644
index df268de..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.spec;
-
-import static androidx.appactions.interaction.capabilities.core.impl.utils.ImmutableCollectors.toImmutableList;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter;
-import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
-import androidx.appactions.interaction.capabilities.core.impl.converters.SlotTypeConverter;
-import androidx.appactions.interaction.capabilities.core.impl.spec.ParamBinding.ArgumentSetter;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-/**
- * A builder for the {@code ActionSpec}.
- */
-public final class ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>,
-        OutputT> {
-
-    private final String mCapabilityName;
-    private final Supplier<ArgumentsBuilderT> mArgumentBuilderSupplier;
-    private final ArrayList<ParamBinding<ArgumentsT, ArgumentsBuilderT>>
-            mParamBindingList = new ArrayList<>();
-    private final Map<String, Function<OutputT, List<ParamValue>>> mOutputBindings =
-            new HashMap<>();
-
-    private ActionSpecBuilder(
-            String capabilityName, Supplier<ArgumentsBuilderT> argumentBuilderSupplier) {
-        this.mCapabilityName = capabilityName;
-        this.mArgumentBuilderSupplier = argumentBuilderSupplier;
-    }
-
-    /**
-     * Creates an empty {@code ActionSpecBuilder} with the given capability name. ArgumentsT is set
-     * to Object as a placeholder, which must be replaced by calling setArgument.
-     */
-    @NonNull
-    public static ActionSpecBuilder<Object, BuilderOf<Object>, Void> ofCapabilityNamed(
-            @NonNull String capabilityName) {
-        return new ActionSpecBuilder<>(capabilityName, () -> Object::new);
-    }
-
-    /** Sets the property type and returns a new {@code ActionSpecBuilder}. */
-
-    /** Sets the argument type and its builder and returns a new {@code ActionSpecBuilder}. */
-    @NonNull
-    public <NewArgumentsT, NewArgumentsBuilderT extends BuilderOf<NewArgumentsT>>
-    ActionSpecBuilder<NewArgumentsT, NewArgumentsBuilderT, OutputT> setArguments(
-            @NonNull Class<NewArgumentsT> unused,
-            @NonNull Supplier<NewArgumentsBuilderT> argumentBuilderSupplier) {
-        return new ActionSpecBuilder<>(this.mCapabilityName, argumentBuilderSupplier);
-    }
-
-    @NonNull
-    public <NewOutputT>
-    ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, NewOutputT> setOutput(
-            @NonNull Class<NewOutputT> unused) {
-        return new ActionSpecBuilder<>(this.mCapabilityName, this.mArgumentBuilderSupplier);
-    }
-
-    /**
-     * Binds the parameter name, getter and setter.
-     *
-     * @param paramName      the name of this action' parameter.
-     * @param paramGetter    a getter of the param-specific info from the property.
-     * @param argumentSetter a setter to the argument with the input from {@code ParamValue}.
-     * @return the builder itself.
-     */
-    @NonNull
-    private ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT>
-    bindParameterInternal(
-            @NonNull String paramName,
-            @NonNull Function<Map<String, Property<?>>,
-                    Optional<IntentParameter>> paramGetter,
-            @NonNull ArgumentSetter<ArgumentsBuilderT> argumentSetter) {
-        mParamBindingList.add(ParamBinding.create(paramName, paramGetter, argumentSetter));
-        return this;
-    }
-
-    /**
-     * Binds the parameter name, getter, and setter for a {@link Property}.
-     *
-     * <p>This parameter is required for any capability built from the generated {@link ActionSpec}.
-     *
-     * @param paramName the name of this action' parameter.
-     * @param propertyGetter a getter of the Property from the property, which must be able to
-     *     fetch a non-null {@code Property} from {@code PropertyT}.
-     * @param paramConsumer a setter to set the string value in the argument builder.
-     * @param paramValueConverter converter FROM assistant ParamValue proto
-     * @param entityConverter converter TO assistant Entity proto
-     * @return the builder itself.
-     */
-    @NonNull
-    public <T, PossibleValueT>
-    ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> bindParameter(
-            @NonNull String paramName,
-            @NonNull
-            Function<Map<String, Property<?>>,
-                    Property<PossibleValueT>>
-                    propertyGetter,
-            @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
-            @NonNull ParamValueConverter<T> paramValueConverter,
-            @NonNull EntityConverter<PossibleValueT> entityConverter) {
-        return bindOptionalParameter(
-                paramName,
-                property -> Optional.of(propertyGetter.apply(property)),
-                paramConsumer,
-                paramValueConverter,
-                entityConverter);
-    }
-
-    /**
-     * Binds the parameter name, getter, and setter for a {@link Property}.
-     *
-     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
-     * If the Property Optional is not set, this parameter will not exist in the parameter
-     * definition of the capability.
-     *
-     * @param paramName the name of this action' parameter.
-     * @param optionalPropertyGetter an optional getter of the Property from the property,
-     *     which may be able to fetch a non-null {@code Property} from {@code PropertyT},
-     *     or get {@link Optional#empty}.
-     * @param paramConsumer a setter to set the string value in the argument builder.
-     * @param paramValueConverter converter FROM assistant ParamValue proto
-     * @param entityConverter converter TO assistant Entity proto
-     * @return the builder itself.
-     */
-    @NonNull
-    public <T, PossibleValueT>
-    ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT>
-    bindOptionalParameter(
-            @NonNull String paramName,
-            @NonNull
-            Function<
-                    Map<String, Property<?>>,
-                    Optional<Property<PossibleValueT>>>
-                    optionalPropertyGetter,
-            @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
-            @NonNull ParamValueConverter<T> paramValueConverter,
-            @NonNull EntityConverter<PossibleValueT> entityConverter) {
-        return bindParameterInternal(
-                paramName,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(p -> buildIntentParameter(paramName, p, entityConverter)),
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofSingular(paramValueConverter)
-                                        .convert(paramList));
-                    }
-                });
-    }
-
-    /**
-     * This is similar to {@link ActionSpecBuilder#bindOptionalParameter} but for setting a list of
-     * entities instead.
-     *
-     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
-     * If the Property Optional is not set, this parameter will not exist in the parameter
-     * definition of the capability.
-     */
-    @NonNull
-    public <T, PossibleValueT>
-    ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT>
-    bindRepeatedParameter(
-            @NonNull String paramName,
-            @NonNull
-            Function<
-                    Map<String, Property<?>>,
-                    Optional<Property<PossibleValueT>>>
-                    optionalPropertyGetter,
-            @NonNull BiConsumer<? super ArgumentsBuilderT, List<T>> paramConsumer,
-            @NonNull ParamValueConverter<T> paramValueConverter,
-            @NonNull EntityConverter<PossibleValueT> entityConverter) {
-        return bindParameterInternal(
-                paramName,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(p -> buildIntentParameter(paramName, p, entityConverter)),
-                (argBuilder, paramList) ->
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofRepeated(paramValueConverter)
-                                        .convert(paramList)));
-    }
-
-    /**
-     * Binds an optional output.
-     *
-     * @param name         the BII output slot name of this parameter.
-     * @param outputGetter a getter of the output from the {@code OutputT} instance.
-     * @param converter    a converter from an output object to a ParamValue.
-     */
-    @NonNull
-    @SuppressWarnings("JdkCollectors")
-    public <T>
-    ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> bindOptionalOutput(
-            @NonNull String name,
-            @NonNull Function<OutputT, Optional<T>> outputGetter,
-            @NonNull Function<T, ParamValue> converter) {
-        mOutputBindings.put(
-                name,
-                output -> {
-                    Optional<T> optionalOut = outputGetter.apply(output);
-                    List<ParamValue> paramValues = new ArrayList<>();
-                    if (optionalOut.isPresent()) {
-                        paramValues.add(converter.apply(optionalOut.get()));
-                    }
-                    return Collections.unmodifiableList(paramValues);
-                });
-        return this;
-    }
-
-    /**
-     * Binds a repeated output.
-     *
-     * @param name         the BII output slot name of this parameter.
-     * @param outputGetter a getter of the output from the {@code OutputT} instance.
-     * @param converter    a converter from an output object to a ParamValue.
-     */
-    @NonNull
-    @SuppressWarnings("JdkCollectors")
-    public <T>
-    ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> bindRepeatedOutput(
-            @NonNull String name,
-            @NonNull Function<OutputT, List<T>> outputGetter,
-            @NonNull Function<T, ParamValue> converter) {
-        mOutputBindings.put(
-                name,
-                output ->
-                        outputGetter.apply(output).stream()
-                                .map(converter)
-                                .collect(toImmutableList()));
-        return this;
-    }
-
-    /** Builds an {@code ActionSpec} from this builder. */
-    @NonNull
-    public ActionSpec<ArgumentsT, OutputT> build() {
-        return new ActionSpecImpl<>(
-                mCapabilityName,
-                mArgumentBuilderSupplier,
-                Collections.unmodifiableList(mParamBindingList),
-                mOutputBindings);
-    }
-
-    /** Create IntentParameter proto from a Property. */
-    @NonNull
-    private static <T> IntentParameter buildIntentParameter(
-            @NonNull String paramName,
-            @NonNull Property<T> property,
-            @NonNull EntityConverter<T> entityConverter) {
-        IntentParameter.Builder builder = IntentParameter.newBuilder()
-                .setName(paramName)
-                .setIsRequired(property.isRequired())
-                .setEntityMatchRequired(property.isValueMatchRequired())
-                .setIsProhibited(property.isProhibited());
-        property.getPossibleValues().stream()
-                .map(entityConverter::convert)
-                .forEach(builder::addPossibleEntities);
-        return builder.build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.kt
new file mode 100644
index 0000000..ad4b7b6
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.spec
+
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.SlotTypeConverter
+import androidx.appactions.interaction.capabilities.core.impl.spec.ParamBinding.ArgumentSetter
+import androidx.appactions.interaction.capabilities.core.impl.spec.ParamBinding.Companion.create
+import androidx.appactions.interaction.capabilities.core.impl.utils.ImmutableCollectors
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.proto.AppActionsContext
+import androidx.appactions.interaction.proto.ParamValue
+import java.util.function.BiConsumer
+import java.util.function.Function
+import java.util.function.Supplier
+
+/**
+ * A builder for the `ActionSpec`.
+ */
+class ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT : BuilderOf<ArgumentsT>, OutputT>
+private constructor(
+    private val capabilityName: String,
+    private val argumentBuilderSupplier: Supplier<ArgumentsBuilderT>
+) {
+    private val paramBindingList: MutableList<ParamBinding<ArgumentsT, ArgumentsBuilderT>> =
+        ArrayList()
+    private val outputBindings: MutableMap<String, Function<OutputT, List<ParamValue>>> = HashMap()
+
+    /** Sets the property type and returns a new `ActionSpecBuilder`.  */
+    /** Sets the argument type and its builder and returns a new `ActionSpecBuilder`.  */
+    @Suppress("UNUSED_PARAMETER")
+    fun <NewArgumentsT, NewArgumentsBuilderT : BuilderOf<NewArgumentsT>> setArguments(
+        unused: Class<NewArgumentsT>,
+        argumentBuilderSupplier: Supplier<NewArgumentsBuilderT>
+    ): ActionSpecBuilder<NewArgumentsT, NewArgumentsBuilderT, OutputT> {
+        return ActionSpecBuilder(this.capabilityName, argumentBuilderSupplier)
+    }
+
+    @Suppress("UNUSED_PARAMETER")
+    fun <NewOutputT> setOutput(
+        unused: Class<NewOutputT>
+    ): ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, NewOutputT> {
+        return ActionSpecBuilder(this.capabilityName, this.argumentBuilderSupplier)
+    }
+
+    /**
+     * Binds the parameter name, getter and setter.
+     *
+     * @param paramName      the name of this action' parameter.
+     * @param paramGetter    a getter of the param-specific info from the property.
+     * @param argumentSetter a setter to the argument with the input from `ParamValue`.
+     * @return the builder itself.
+     */
+    private fun bindParameterInternal(
+        paramName: String,
+        paramGetter: Function<Map<String, Property<*>>, AppActionsContext.IntentParameter?>,
+        argumentSetter: ArgumentSetter<ArgumentsBuilderT>
+    ): ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> {
+        paramBindingList.add(create(paramName, paramGetter, argumentSetter))
+        return this
+    }
+
+    /**
+     * Binds the parameter name, getter, and setter for a [Property].
+     *
+     * If the Property getter returns a null value, this parameter will not exist in the parameter
+     * definition of the capability.
+     *
+     * @param paramName the name of this action' parameter.
+     * @param propertyGetter a getter of the Property from the property, which must be able to
+     * fetch a non-null `Property` from `PropertyT`.
+     * @param paramConsumer a setter to set the string value in the argument builder.
+     * @param paramValueConverter converter FROM assistant ParamValue proto
+     * @param entityConverter converter TO assistant Entity proto
+     * @return the builder itself.
+     */
+    fun <T, PossibleValueT> bindParameter(
+        paramName: String,
+        propertyGetter: Function<Map<String, Property<*>>, Property<PossibleValueT>?>,
+        paramConsumer: BiConsumer<in ArgumentsBuilderT, T>,
+        paramValueConverter: ParamValueConverter<T>,
+        entityConverter: EntityConverter<PossibleValueT>
+    ): ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> {
+        return bindParameterInternal(
+            paramName,
+            { propertyMap ->
+                propertyGetter.apply(propertyMap)?.let {
+                    buildIntentParameter(paramName, it, entityConverter)
+                }
+            },
+            { argBuilder: ArgumentsBuilderT, paramList: List<ParamValue> ->
+                if (paramList.isNotEmpty()) {
+                    paramConsumer.accept(
+                        argBuilder,
+                        SlotTypeConverter.ofSingular(paramValueConverter).convert(paramList)
+                    )
+                }
+            }
+        )
+    }
+
+    /**
+     * This is similar to [ActionSpecBuilder.bindParameter] but for setting a list of
+     * entities instead.
+     *
+     * If the Property getter returns a null value, this parameter will not exist in the parameter
+     * definition of the capability.
+     */
+    fun <T, PossibleValueT> bindRepeatedParameter(
+        paramName: String,
+        propertyGetter: Function<Map<String, Property<*>>, Property<PossibleValueT>?>,
+        paramConsumer: BiConsumer<in ArgumentsBuilderT, List<T>>,
+        paramValueConverter: ParamValueConverter<T>,
+        entityConverter: EntityConverter<PossibleValueT>
+    ): ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> {
+        return bindParameterInternal(
+            paramName,
+            { propertyMap ->
+                propertyGetter.apply(propertyMap)?.let {
+                    buildIntentParameter(paramName, it, entityConverter)
+                }
+            },
+            { argBuilder: ArgumentsBuilderT, paramList: List<ParamValue?>? ->
+                paramConsumer.accept(
+                    argBuilder,
+                    SlotTypeConverter.ofRepeated(paramValueConverter).convert(paramList!!)
+                )
+            }
+        )
+    }
+
+    /**
+     * Binds an optional output.
+     *
+     * @param name         the BII output slot name of this parameter.
+     * @param outputFieldGetter a getter of the output from the `OutputT` instance.
+     * @param converter    a converter from an output object to a ParamValue.
+     */
+    fun <T> bindOutput(
+        name: String,
+        outputFieldGetter: Function<OutputT, T?>,
+        converter: Function<T, ParamValue>
+    ): ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> {
+        outputBindings[name] = Function { output: OutputT ->
+            val outputField: T? = outputFieldGetter.apply(output)
+            val paramValues: MutableList<ParamValue> =
+                ArrayList()
+            if (outputField != null) {
+                paramValues.add(converter.apply(outputField))
+            }
+            paramValues.toList()
+        }
+        return this
+    }
+
+    /**
+     * Binds a repeated output.
+     *
+     * @param name         the BII output slot name of this parameter.
+     * @param outputGetter a getter of the output from the `OutputT` instance.
+     * @param converter    a converter from an output object to a ParamValue.
+     */
+    fun <T> bindRepeatedOutput(
+        name: String,
+        outputGetter: Function<OutputT, List<T>>,
+        converter: Function<T, ParamValue>
+    ): ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> {
+        outputBindings[name] = Function { output: OutputT ->
+            outputGetter.apply(output).stream()
+                .map(converter)
+                .collect(ImmutableCollectors.toImmutableList())
+        }
+        return this
+    }
+
+    /** Builds an `ActionSpec` from this builder.  */
+    fun build(): ActionSpec<ArgumentsT, OutputT> {
+        return ActionSpecImpl(
+            capabilityName,
+            argumentBuilderSupplier,
+            paramBindingList.toList(),
+            outputBindings.toMap()
+        )
+    }
+
+    companion object {
+        /**
+         * Creates an empty `ActionSpecBuilder` with the given capability name. ArgumentsT is set
+         * to Object as a placeholder, which must be replaced by calling setArgument.
+         */
+        fun ofCapabilityNamed(
+            capabilityName: String
+        ): ActionSpecBuilder<Any, BuilderOf<Any>, Any> {
+            return ActionSpecBuilder(capabilityName) { BuilderOf { Object() } }
+        }
+
+        /** Create IntentParameter proto from a Property.  */
+        internal fun <T> buildIntentParameter(
+            paramName: String,
+            property: Property<T>,
+            entityConverter: EntityConverter<T>
+        ): AppActionsContext.IntentParameter {
+            val builder = AppActionsContext.IntentParameter.newBuilder()
+                .setName(paramName)
+                .setIsRequired(property.isRequired)
+                .setEntityMatchRequired(property.isValueMatchRequired)
+                .setIsProhibited(property.isProhibited)
+            property.possibleValues.stream()
+                .map { possibleValue ->
+                    entityConverter.convert(possibleValue)
+                }
+                .forEach { entityProto ->
+                    builder.addPossibleEntities(entityProto)
+                }
+            return builder.build()
+        }
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
deleted file mode 100644
index 8dfdb19..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.spec;
-
-import static androidx.appactions.interaction.capabilities.core.impl.utils.ImmutableCollectors.toImmutableList;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.proto.AppActionsContext.AppAction;
-import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-/** The implementation of {@code ActionSpec} interface. */
-final class ActionSpecImpl<
-        ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>, OutputT>
-        implements ActionSpec<ArgumentsT, OutputT> {
-
-    private final String mCapabilityName;
-    private final Supplier<ArgumentsBuilderT> mArgumentBuilderSupplier;
-    private final List<ParamBinding<ArgumentsT, ArgumentsBuilderT>> mParamBindingList;
-    private final Map<String, Function<OutputT, List<ParamValue>>> mOutputBindings;
-
-    ActionSpecImpl(
-            String capabilityName,
-            Supplier<ArgumentsBuilderT> argumentBuilderSupplier,
-            List<ParamBinding<ArgumentsT, ArgumentsBuilderT>> paramBindingList,
-            Map<String, Function<OutputT, List<ParamValue>>> outputBindings) {
-        this.mCapabilityName = capabilityName;
-        this.mArgumentBuilderSupplier = argumentBuilderSupplier;
-        this.mParamBindingList = paramBindingList;
-        this.mOutputBindings = outputBindings;
-    }
-
-    @NonNull
-    @Override
-    public AppAction convertPropertyToProto(@NonNull Map<String, Property<?>> property) {
-        return AppAction.newBuilder()
-                .setName(mCapabilityName)
-                .addAllParams(
-                        mParamBindingList.stream()
-                                .map(binding -> binding.paramGetter().apply(property))
-                                .filter(Optional::isPresent)
-                                .map(Optional::get)
-                                .collect(toImmutableList()))
-                .build();
-    }
-
-    @NonNull
-    @Override
-    public ArgumentsT buildArguments(@NonNull Map<String, List<ParamValue>> args)
-            throws StructConversionException {
-        ArgumentsBuilderT argumentBuilder = mArgumentBuilderSupplier.get();
-        for (ParamBinding<ArgumentsT, ArgumentsBuilderT> binding : mParamBindingList) {
-            List<ParamValue> paramValues = args.get(binding.name());
-            if (paramValues == null) {
-                continue;
-            }
-            try {
-                binding.argumentSetter().setArguments(argumentBuilder, paramValues);
-            } catch (StructConversionException e) {
-                // Wrap the exception with a more meaningful error message.
-                throw new StructConversionException(
-                        String.format(
-                                "Failed to parse parameter '%s' from assistant because of "
-                                        + "failure: %s",
-                                binding.name(), e.getMessage()));
-            }
-        }
-        return argumentBuilder.build();
-    }
-
-    @NonNull
-    @Override
-    public StructuredOutput convertOutputToProto(OutputT output) {
-        StructuredOutput.Builder outputBuilder = StructuredOutput.newBuilder();
-        for (Map.Entry<String, Function<OutputT, List<ParamValue>>> entry :
-                mOutputBindings.entrySet()) {
-            List<ParamValue> values = entry.getValue().apply(output);
-            if (!values.isEmpty()) {
-                outputBuilder.addOutputValues(
-                        StructuredOutput.OutputValue.newBuilder()
-                                .setName(entry.getKey())
-                                .addAllValues(values)
-                                .build());
-            }
-        }
-        return outputBuilder.build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.kt
new file mode 100644
index 0000000..a05c8ed
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.spec
+
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.impl.utils.ImmutableCollectors
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.proto.AppActionsContext
+import androidx.appactions.interaction.proto.FulfillmentResponse
+import androidx.appactions.interaction.proto.ParamValue
+import java.util.function.Function
+import java.util.function.Supplier
+
+/** The implementation of `ActionSpec` interface.  */
+internal class ActionSpecImpl<ArgumentsT, ArgumentsBuilderT : BuilderOf<ArgumentsT>, OutputT>(
+    private val capabilityName: String,
+    private val argumentBuilderSupplier: Supplier<ArgumentsBuilderT>,
+    private val paramBindingList: List<ParamBinding<ArgumentsT, ArgumentsBuilderT>>,
+    private val outputBindings: Map<String, Function<OutputT, List<ParamValue>>>
+) : ActionSpec<ArgumentsT, OutputT> {
+
+    override fun convertPropertyToProto(
+        property: Map<String, Property<*>>
+    ): AppActionsContext.AppAction {
+        return AppActionsContext.AppAction.newBuilder()
+            .setName(capabilityName)
+            .addAllParams(
+                paramBindingList.stream()
+                    .map { binding -> binding.propertyConverter.apply(property) }
+                    .filter { intentParam -> intentParam != null }
+                    .collect(ImmutableCollectors.toImmutableList())
+            )
+            .build()
+    }
+
+    @Throws(StructConversionException::class)
+    override fun buildArguments(args: Map<String, List<ParamValue>>): ArgumentsT {
+        val argumentBuilder = argumentBuilderSupplier.get()
+        paramBindingList.forEach { binding ->
+            val paramValues = args[binding.name] ?: return@forEach
+            try {
+                binding.argumentSetter.setArguments(argumentBuilder, paramValues)
+            } catch (e: StructConversionException) {
+                // Wrap the exception with a more meaningful error message.
+                throw StructConversionException(
+                    "Failed to parse parameter '${binding.name}' from assistant because of" +
+                        " failure: ${e.message}"
+                )
+            }
+        }
+        return argumentBuilder.build()
+    }
+
+    override fun convertOutputToProto(output: OutputT): FulfillmentResponse.StructuredOutput {
+        val outputBuilder = FulfillmentResponse.StructuredOutput.newBuilder()
+        outputBindings.entries.forEach { entry ->
+            val paramValues = entry.value.apply(output)
+            if (paramValues.isNotEmpty()) {
+                outputBuilder.addOutputValues(
+                    FulfillmentResponse.StructuredOutput.OutputValue.newBuilder()
+                        .setName(entry.key)
+                        .addAllValues(paramValues)
+                        .build()
+                )
+            }
+        }
+        return outputBuilder.build()
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java
deleted file mode 100644
index 87ce323..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.spec;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-
-/**
- * A binding between a parameter and its Property converter / Argument setter.
- */
-@AutoValue
-public abstract class ParamBinding<
-        ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>> {
-
-    static <ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>>
-    ParamBinding<ArgumentsT, ArgumentsBuilderT> create(
-            String name,
-            Function<Map<String, Property<?>>,
-                    Optional<IntentParameter>> paramGetter,
-            ArgumentSetter<ArgumentsBuilderT> argumentSetter) {
-        return new AutoValue_ParamBinding<>(name, paramGetter, argumentSetter);
-    }
-
-    /** Returns the name of this param. */
-    @NonNull
-    public abstract String name();
-
-    /**
-     * Converts a {@code Property Map} to an {@code IntentParameter} proto. The resulting proto is
-     * the
-     * format which we send the current params to Assistant (via. app actions context).
-     */
-    @NonNull
-    public abstract Function<Map<String, Property<?>>,
-            Optional<IntentParameter>> paramGetter();
-
-    /**
-     * Populates the {@code ArgumentsBuilderT} for this param with the {@code ParamValue} sent from
-     * Assistant in Fulfillment.
-     */
-    @NonNull
-    public abstract ArgumentSetter<ArgumentsBuilderT> argumentSetter();
-
-    /**
-     * Given a {@code List<ParamValue>}, convert it to user-visible type and set it into
-     * ArgumentBuilder.
-     *
-     * @param <ArgumentsBuilderT>
-     */
-    @FunctionalInterface
-    public interface ArgumentSetter<ArgumentsBuilderT> {
-
-        /** Conversion from protos to user-visible type. */
-        void setArguments(@NonNull ArgumentsBuilderT builder, @NonNull List<ParamValue> paramValues)
-                throws StructConversionException;
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.kt
new file mode 100644
index 0000000..7e57ead
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.spec
+
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter
+import androidx.appactions.interaction.proto.ParamValue
+import java.util.function.Function
+
+data class ParamBinding<ArgumentsT, ArgumentsBuilderT : BuilderOf<ArgumentsT>>
+internal constructor(
+    val name: String,
+    val propertyConverter: Function<Map<String, Property<*>>, IntentParameter?>,
+    val argumentSetter: ArgumentSetter<ArgumentsBuilderT>
+) {
+    /**
+     * Given a `List<ParamValue>`, convert it to user-visible type and set it into
+     * ArgumentBuilder.
+     */
+    fun interface ArgumentSetter<ArgumentsBuilderT> {
+        /** Conversion from protos to user-visible type.  */
+        @Throws(StructConversionException::class)
+        fun setArguments(builder: ArgumentsBuilderT, paramValues: List<ParamValue>)
+    }
+
+    companion object {
+        @JvmStatic
+        fun <ArgumentsT, ArgumentsBuilderT : BuilderOf<ArgumentsT>> create(
+            name: String,
+            paramGetter: Function<Map<String, Property<*>>, IntentParameter?>,
+            argumentSetter: ArgumentSetter<ArgumentsBuilderT>
+        ): ParamBinding<ArgumentsT, ArgumentsBuilderT> {
+            return ParamBinding(name, paramGetter, argumentSetter)
+        }
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/AppGroundingResult.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/AppGroundingResult.java
deleted file mode 100644
index 9145141..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/AppGroundingResult.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.task;
-
-import androidx.appactions.interaction.proto.CurrentValue;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import com.google.auto.value.AutoOneOf;
-
-/**
- * Helper object to express if the grounding step of argument processing was successul for a single
- * ParamValue.
- */
-@AutoOneOf(AppGroundingResult.Kind.class)
-abstract class AppGroundingResult {
-    static AppGroundingResult ofSuccess(ParamValue paramValue) {
-        return AutoOneOf_AppGroundingResult.success(paramValue);
-    }
-
-    static AppGroundingResult ofFailure(CurrentValue currentValue) {
-        return AutoOneOf_AppGroundingResult.failure(currentValue);
-    }
-
-    public abstract Kind getKind();
-
-    abstract ParamValue success();
-
-    abstract CurrentValue failure();
-
-    enum Kind {
-        SUCCESS,
-        FAILURE
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/AppGroundingResult.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/AppGroundingResult.kt
new file mode 100644
index 0000000..f2463cd
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/AppGroundingResult.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.task
+
+import androidx.appactions.interaction.proto.CurrentValue
+import androidx.appactions.interaction.proto.ParamValue
+
+data class AppGroundingResult private constructor(
+    val kind: Kind,
+    val success: ParamValue?,
+    val failure: CurrentValue?
+) {
+    enum class Kind {
+        SUCCESS,
+        FAILURE
+    }
+
+    companion object {
+        @JvmStatic
+        fun ofSuccess(paramValue: ParamValue) = AppGroundingResult(Kind.SUCCESS, paramValue, null)
+
+        @JvmStatic
+        fun ofFailure(currentValue: CurrentValue) =
+            AppGroundingResult(Kind.FAILURE, null, currentValue)
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/GenericResolverInternal.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/GenericResolverInternal.kt
index 591d9bb..32813dc 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/GenericResolverInternal.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/GenericResolverInternal.kt
@@ -94,20 +94,16 @@
         paramValues: List<ParamValue>,
         converter: ParamValueConverter<ValueTypeT>
     ): ValidationResult {
-        val singularConverter = SlotTypeConverter.ofSingular(converter)
-        val repeatedConverter = SlotTypeConverter.ofRepeated(converter)
+        val singularValue = SlotTypeConverter.ofSingular(converter).convert(paramValues)
+        val repeatedValues = SlotTypeConverter.ofRepeated(converter).convert(paramValues)
         return invokeExternalSuspendBlock("onReceived") {
             when {
-                value != null -> value.onReceived(singularConverter.convert(paramValues))
-                valueList != null -> valueList.onReceived(repeatedConverter.convert(paramValues))
-                appEntity != null ->
-                    appEntity.onReceived(singularConverter.convert(paramValues))
-                appEntityList != null ->
-                    appEntityList.onReceived(repeatedConverter.convert(paramValues))
-                inventory != null ->
-                    inventory.onReceived(singularConverter.convert(paramValues))
-                inventoryList != null ->
-                    inventoryList.onReceived(repeatedConverter.convert(paramValues))
+                value != null -> value.onReceived(singularValue)
+                valueList != null -> valueList.onReceived(repeatedValues)
+                appEntity != null -> appEntity.onReceived(singularValue)
+                appEntityList != null -> appEntityList.onReceived(repeatedValues)
+                inventory != null -> inventory.onReceived(singularValue)
+                inventoryList != null -> inventoryList.onReceived(repeatedValues)
                 else -> throw IllegalStateException("unreachable")
             }
         }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/OnReadyToConfirmListener.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/OnReadyToConfirmListener.kt
new file mode 100644
index 0000000..ed30805
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/OnReadyToConfirmListener.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.impl.task
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.ConfirmationOutput
+
+/**
+ * Generic onReadyToConfirm listener for a task capability.
+ * This should wrap only the external Session#onReadyToConfirm and nothing else.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface OnReadyToConfirmListener<ArgumentsT, ConfirmationT> {
+    /** onReadyToConfirm callback for a task session. */
+    suspend fun onReadyToConfirm(arguments: ArgumentsT): ConfirmationOutput<ConfirmationT>
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/OnReadyToConfirmListenerInternal.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/OnReadyToConfirmListenerInternal.kt
deleted file mode 100644
index e9adb16..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/OnReadyToConfirmListenerInternal.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.appactions.interaction.capabilities.core.impl.task
-
-import androidx.appactions.interaction.capabilities.core.ConfirmationOutput
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
-import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.MissingRequiredArgException
-import androidx.appactions.interaction.proto.ParamValue
-
-/**
- * Generic onReadyToConfirm listener for a task capability. This is the entry point to specific
- * onReadyToConfirm listeners. For example, Search/Update sub-BIIs factories may invoke specific
- * onReadyToConfirm listeners for that BII.
- */
-interface OnReadyToConfirmListenerInternal<ConfirmationT> {
-    /** onReadyToConfirm callback for a task capability. */
-    @Throws(StructConversionException::class, MissingRequiredArgException::class)
-    suspend fun onReadyToConfirm(
-        args: Map<String, List<ParamValue>>
-    ): ConfirmationOutput<ConfirmationT>
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt
index 6242b6f8..a31759c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/SessionBridge.kt
@@ -26,7 +26,10 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 fun interface SessionBridge<
     ExecutionSessionT,
+    ArgumentsT,
     ConfirmationT
 > {
-    fun createTaskHandler(externalSession: ExecutionSessionT): TaskHandler<ConfirmationT>
+    fun createTaskHandler(
+        externalSession: ExecutionSessionT
+    ): TaskHandler<ArgumentsT, ConfirmationT>
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
index fc9eeaf..4dee5fa 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
@@ -46,7 +46,7 @@
     private val actionSpec: ActionSpec<ArgumentsT, OutputT>,
     private val property: Map<String, Property<*>>,
     private val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSessionT?,
-    private val sessionBridge: SessionBridge<ExecutionSessionT, ConfirmationT>,
+    private val sessionBridge: SessionBridge<ExecutionSessionT, ArgumentsT, ConfirmationT>,
     private val sessionUpdaterSupplier: Supplier<SessionUpdaterT>
 ) : Capability(id) {
 
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt
index 1f577f5..6ef8d2b 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt
@@ -41,7 +41,7 @@
     override val sessionId: String,
     actionSpec: ActionSpec<ArgumentsT, OutputT>,
     appAction: AppAction,
-    taskHandler: TaskHandler<ConfirmationT>,
+    taskHandler: TaskHandler<ArgumentsT, ConfirmationT>,
     externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
     private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
 ) : CapabilitySession, TaskUpdateHandler {
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskHandler.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskHandler.kt
index 8b71429..9601c24 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskHandler.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskHandler.kt
@@ -29,28 +29,28 @@
 
 /** Container of multi-turn Task related function references. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-data class TaskHandler<ConfirmationT>
+data class TaskHandler<ArgumentsT, ConfirmationT>
 internal constructor(
     internal val taskParamMap: Map<String, TaskParamBinding<*>>,
     internal val confirmationDataBindings: Map<String, (ConfirmationT) -> List<ParamValue>>,
-    internal val onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>?,
+    internal val onReadyToConfirmListener: OnReadyToConfirmListener<ArgumentsT, ConfirmationT>?,
 ) {
-    class Builder<ConfirmationT>() {
+    class Builder<ArgumentsT, ConfirmationT>() {
         private val mutableTaskParamMap = mutableMapOf<String, TaskParamBinding<*>>()
         private val confirmationDataBindings =
             mutableMapOf<String, (ConfirmationT) -> List<ParamValue>>()
-        private var onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>? =
+        private var onReadyToConfirmListener: OnReadyToConfirmListener<ArgumentsT, ConfirmationT>? =
             null
 
         fun <ValueTypeT> registerInventoryTaskParam(
             paramName: String,
             listener: InventoryListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             mutableTaskParamMap[paramName] =
                 TaskParamBinding(
                     paramName,
-                    GROUND_IF_NO_IDENTIFIER,
+                    GROUND_NEVER,
                     GenericResolverInternal.fromInventoryListener(listener),
                     converter,
                     null,
@@ -62,11 +62,11 @@
             paramName: String,
             listener: InventoryListListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             mutableTaskParamMap[paramName] =
                 TaskParamBinding(
                     paramName,
-                    GROUND_IF_NO_IDENTIFIER,
+                    GROUND_NEVER,
                     GenericResolverInternal.fromInventoryListListener(listener),
                     converter,
                     null,
@@ -80,7 +80,7 @@
             converter: ParamValueConverter<ValueTypeT>,
             entityConverter: EntityConverter<ValueTypeT>,
             searchActionConverter: SearchActionConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             mutableTaskParamMap[paramName] =
                 TaskParamBinding(
                     paramName,
@@ -98,7 +98,7 @@
             converter: ParamValueConverter<ValueTypeT>,
             entityConverter: EntityConverter<ValueTypeT>,
             searchActionConverter: SearchActionConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             mutableTaskParamMap[paramName] =
                 TaskParamBinding(
                     paramName,
@@ -114,7 +114,7 @@
             paramName: String,
             listener: ValueListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             mutableTaskParamMap[paramName] =
                 TaskParamBinding(
                     paramName,
@@ -130,7 +130,7 @@
             paramName: String,
             listener: ValueListener<List<ValueTypeT>>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             mutableTaskParamMap[paramName] =
                 TaskParamBinding(
                     paramName,
@@ -154,26 +154,25 @@
             paramName: String,
             confirmationGetter: (ConfirmationT) -> T?,
             converter: (T) -> ParamValue,
-        ): Builder<ConfirmationT> = apply {
+        ) = apply {
             confirmationDataBindings[paramName] = { output: ConfirmationT ->
                 listOfNotNull(confirmationGetter(output)).map(converter)
             }
         }
 
-        /** Sets the onReadyToConfirmListener for this capability. */
-        fun setOnReadyToConfirmListenerInternal(
-            onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>,
-        ): Builder<ConfirmationT> = apply {
+        /** Sets the onReadyToConfirmListener for this session. */
+        fun setOnReadyToConfirmListener(
+            onReadyToConfirmListener: OnReadyToConfirmListener<ArgumentsT, ConfirmationT>,
+        ) = apply {
             this.onReadyToConfirmListener = onReadyToConfirmListener
         }
 
-        fun build(): TaskHandler<ConfirmationT> {
-            return TaskHandler(
-                mutableTaskParamMap.toMap(),
-                confirmationDataBindings,
-                onReadyToConfirmListener,
-            )
-        }
+        fun build() = TaskHandler(
+            mutableTaskParamMap.toMap(),
+            confirmationDataBindings,
+            onReadyToConfirmListener,
+        )
+
         companion object {
             val GROUND_IF_NO_IDENTIFIER = { paramValue: ParamValue -> !paramValue.hasIdentifier() }
             val GROUND_NEVER = { _: ParamValue -> false }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt
index 5b51ac3..3b4ca84 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt
@@ -29,10 +29,10 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.InvalidResolverException
 import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.MissingEntityConverterException
-import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.MissingRequiredArgException
 import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.MissingSearchActionConverterException
 import androidx.appactions.interaction.capabilities.core.impl.utils.CapabilityLogger
 import androidx.appactions.interaction.capabilities.core.impl.utils.LoggerInternal
+import androidx.appactions.interaction.capabilities.core.impl.utils.handleExceptionFromRequestProcessing
 import androidx.appactions.interaction.capabilities.core.impl.utils.invokeExternalBlock
 import androidx.appactions.interaction.capabilities.core.impl.utils.invokeExternalSuspendBlock
 import androidx.appactions.interaction.proto.AppActionsContext
@@ -60,7 +60,7 @@
     private val sessionId: String,
     private val actionSpec: ActionSpec<ArgumentsT, OutputT>,
     private val appAction: AppActionsContext.AppAction,
-    private val taskHandler: TaskHandler<ConfirmationT>,
+    private val taskHandler: TaskHandler<ArgumentsT, ConfirmationT>,
     private val externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
     private val scope: CoroutineScope,
 ) {
@@ -181,27 +181,27 @@
     ) = withUiHandleRegistered {
         val argumentsWrapper = assistantUpdateRequest.argumentsWrapper
         val callback = assistantUpdateRequest.callbackInternal
-        val fulfillmentResult: FulfillmentResult
-        if (argumentsWrapper.requestMetadata == null) {
-            fulfillmentResult = FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST)
-        } else {
-            fulfillmentResult = when (argumentsWrapper.requestMetadata.requestType()) {
-                FulfillmentRequest.Fulfillment.Type.UNRECOGNIZED,
-                FulfillmentRequest.Fulfillment.Type.UNKNOWN_TYPE,
-                ->
-                    FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST)
-
+        try {
+            val fulfillmentResult: FulfillmentResult = when (
+                argumentsWrapper.requestMetadata?.requestType
+            ) {
                 FulfillmentRequest.Fulfillment.Type.SYNC -> handleSync(argumentsWrapper)
                 FulfillmentRequest.Fulfillment.Type.CONFIRM -> handleConfirm()
-                FulfillmentRequest.Fulfillment.Type.CANCEL
-                -> {
+                FulfillmentRequest.Fulfillment.Type.CANCEL -> {
                     terminate()
                     FulfillmentResult(FulfillmentResponse.getDefaultInstance())
                 }
                 else -> FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST)
             }
+            fulfillmentResult.applyToCallback(callback)
+        } catch (t: Throwable) {
+            LoggerInternal.log(
+                CapabilityLogger.LogLevel.ERROR,
+                LOG_TAG,
+                "Assistant request processing failed",
+            )
+            handleExceptionFromRequestProcessing(t, callback::onError)
         }
-        fulfillmentResult.applyToCallback(callback)
     }
 
     private suspend fun processTouchEventUpdateRequest(
@@ -248,15 +248,16 @@
             }
         } catch (t: Throwable) {
             LoggerInternal.log(CapabilityLogger.LogLevel.ERROR, LOG_TAG, "Manual input fail")
-            if (touchEventCallback != null) {
-                touchEventCallback!!.onError(ErrorStatusInternal.TOUCH_EVENT_REQUEST_FAILURE)
-            } else {
+            if (touchEventCallback == null) {
                 LoggerInternal.log(
                     CapabilityLogger.LogLevel.ERROR,
                     LOG_TAG,
                     "Manual input null callback",
                 )
             }
+            handleExceptionFromRequestProcessing(t) {
+                touchEventCallback?.onError(it)
+            }
         }
     }
 
@@ -270,7 +271,7 @@
      *
      * Otherwise, the future contains a FulfillmentResponse containing BIC or BIO data.
      */
-    @Throws(StructConversionException::class, MissingRequiredArgException::class)
+    @Throws(StructConversionException::class)
     private suspend fun maybeConfirmOrExecute(): FulfillmentResponse {
         val finalArguments = getCurrentAcceptedArguments()
         if (
@@ -288,8 +289,9 @@
 
     private fun maybeInitializeTask() {
         if (status === Status.UNINITIATED) {
+            val sessionConfig = SessionConfig()
             invokeExternalBlock("onCreate") {
-                externalSession.onCreate(SessionConfig())
+                externalSession.onCreate(sessionConfig)
             }
         }
         status = Status.IN_PROGRESS
@@ -302,18 +304,12 @@
      * turn, so the logic should include onEnter, arg validation, and onExit.
      */
     private suspend fun handleSync(argumentsWrapper: ArgumentsWrapper): FulfillmentResult {
-        return try {
-            maybeInitializeTask()
-            clearMissingArgs(argumentsWrapper)
-            processFulfillmentValues(argumentsWrapper.paramValues)
-            val fulfillmentResponse = maybeConfirmOrExecute()
-            LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task sync success")
-            FulfillmentResult(fulfillmentResponse)
-        } catch (t: Throwable) {
-            // TODO(b/276354491) implement fine-grained exception handling
-            LoggerInternal.log(CapabilityLogger.LogLevel.ERROR, LOG_TAG, "Task sync fail", t)
-            FulfillmentResult(ErrorStatusInternal.SYNC_REQUEST_FAILURE)
-        }
+        maybeInitializeTask()
+        clearMissingArgs(argumentsWrapper)
+        processFulfillmentValues(argumentsWrapper.paramValues)
+        val fulfillmentResponse = maybeConfirmOrExecute()
+        LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task sync success")
+        return FulfillmentResult(fulfillmentResponse)
     }
 
     /**
@@ -322,15 +318,9 @@
      */
     private suspend fun handleConfirm(): FulfillmentResult {
         val finalArguments = getCurrentAcceptedArguments()
-        return try {
-            val fulfillmentResponse = getFulfillmentResponseForExecution(finalArguments)
-            LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task confirm success")
-            FulfillmentResult(fulfillmentResponse)
-        } catch (t: Throwable) {
-            // TODO(b/276354491) implement fine-grained exception handling
-            LoggerInternal.log(CapabilityLogger.LogLevel.ERROR, LOG_TAG, "Task confirm fail")
-            FulfillmentResult(ErrorStatusInternal.CONFIRMATION_REQUEST_FAILURE)
-        }
+        val fulfillmentResponse = getFulfillmentResponseForExecution(finalArguments)
+        LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task confirm success")
+        return FulfillmentResult(fulfillmentResponse)
     }
 
     private fun clearMissingArgs(assistantArgs: ArgumentsWrapper) {
@@ -351,7 +341,6 @@
      * validation) are executed asynchronously.
      */
     @Throws(
-        MissingEntityConverterException::class,
         MissingSearchActionConverterException::class,
         StructConversionException::class,
         InvalidResolverException::class,
@@ -457,12 +446,16 @@
             }
         }
 
-    @Throws(StructConversionException::class, MissingRequiredArgException::class)
+    @Throws(StructConversionException::class)
     private suspend fun getFulfillmentResponseForConfirmation(
         finalArguments: Map<String, List<ParamValue>>,
     ): FulfillmentResponse {
+        val arguments = actionSpec.buildArguments(finalArguments)
+        requireNotNull(taskHandler.onReadyToConfirmListener) {
+            "caller must ensure TaskHandler.onReadyToConfirmListener is not null"
+        }
         val result = invokeExternalSuspendBlock("onReadyToConfirm") {
-            taskHandler.onReadyToConfirmListener!!.onReadyToConfirm(finalArguments)
+            taskHandler.onReadyToConfirmListener.onReadyToConfirm(arguments)
         }
         val fulfillmentResponse = FulfillmentResponse.newBuilder()
         convertToConfirmationOutput(result)?.let { fulfillmentResponse.confirmationData = it }
@@ -473,8 +466,9 @@
     private suspend fun getFulfillmentResponseForExecution(
         finalArguments: Map<String, List<ParamValue>>,
     ): FulfillmentResponse {
+        val arguments = actionSpec.buildArguments(finalArguments)
         val result = invokeExternalSuspendBlock("onExecute") {
-            externalSession.onExecute(actionSpec.buildArguments(finalArguments))
+            externalSession.onExecute(arguments)
         }
         terminate()
         val fulfillmentResponse =
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskSlotProcessor.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskSlotProcessor.kt
index 1ab2990..fee2fd0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskSlotProcessor.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskSlotProcessor.kt
@@ -25,7 +25,6 @@
 import androidx.appactions.interaction.proto.CurrentValue
 import androidx.appactions.interaction.proto.DisambiguationData
 import androidx.appactions.interaction.proto.ParamValue
-import kotlin.IllegalStateException
 import kotlin.String
 import kotlin.Throws
 
@@ -114,7 +113,6 @@
                 )
             }
             AppGroundingResult.Kind.FAILURE -> AppGroundingResult.ofFailure(pendingValue)
-            else -> throw IllegalStateException("unreachable")
         }
 
     /** enqueues processing of a pending value that requires app-driven grounding. */
@@ -132,7 +130,6 @@
         when (groundingResult.kind) {
             AppGroundingResult.Kind.SUCCESS -> ground(pendingValue.value, taskParamBinding)
             AppGroundingResult.Kind.FAILURE -> AppGroundingResult.ofFailure(pendingValue)
-            else -> throw IllegalStateException("unreachable")
         }
 
     /**
@@ -149,9 +146,8 @@
         ungroundedValues: MutableList<CurrentValue>,
     ): AppGroundingResult {
         when (groundingResult.kind) {
-            AppGroundingResult.Kind.SUCCESS -> groundedValues.add(groundingResult.success())
-            AppGroundingResult.Kind.FAILURE -> ungroundedValues.add(groundingResult.failure())
-            else -> throw IllegalStateException("unreachable")
+            AppGroundingResult.Kind.SUCCESS -> groundedValues.add(groundingResult.success!!)
+            AppGroundingResult.Kind.FAILURE -> ungroundedValues.add(groundingResult.failure!!)
         }
         return groundingResult
     }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/DisambigStateException.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/DisambigStateException.java
index 9f06580..af225a1 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/DisambigStateException.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/DisambigStateException.java
@@ -17,13 +17,14 @@
 package androidx.appactions.interaction.capabilities.core.impl.task.exceptions;
 
 import androidx.annotation.NonNull;
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.InvalidRequestException;
 
 /**
  * Represents an internal issue with the state sync between the SDK and Assistant. One example is
  * when the SDK places an argument in dismabig state, but then Assistant sends the same argument
  * data again without any grounding.
  */
-public final class DisambigStateException extends Exception {
+public final class DisambigStateException extends InvalidRequestException {
 
     public DisambigStateException(@NonNull String message) {
         super(message);
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/InvalidResolverException.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/InvalidResolverException.java
index 543840c..e6f5328 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/InvalidResolverException.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/InvalidResolverException.java
@@ -20,8 +20,8 @@
 
 /**
  * Represents an internal issue with Resolvers, such as an "app-driven" method being invoked on a
- * "assistant-driven" resolver.
- */
+* "assistant-driven" resolver.
+*/
 public final class InvalidResolverException extends Exception {
 
     public InvalidResolverException(@NonNull String message) {
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java
deleted file mode 100644
index 32b3fda..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/exceptions/MissingRequiredArgException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.task.exceptions;
-
-import androidx.annotation.NonNull;
-
-/**
- * During the onExecuteListener handling, all required params should be present in the Map sent to
- * the listener. If they are not for some reason, this is an internal error.
- */
-public final class MissingRequiredArgException extends Exception {
-
-    public MissingRequiredArgException(@NonNull String message) {
-        super(message);
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt
index 1811abd..cd36c47 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt
@@ -18,10 +18,14 @@
 
 import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.ExternalException
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.InvalidRequestException
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.DisambigStateException
+import androidx.appactions.interaction.capabilities.core.impl.task.exceptions.InvalidResolverException
 import kotlin.reflect.KClass
 
+private const val LOG_TAG = "CallbackUtils"
+
 /** invoke an externally implemented method, wrapping any exceptions with ExternalException.
  */
 internal fun <T> invokeExternalBlock(description: String, block: () -> T): T {
@@ -47,14 +51,17 @@
 }
 
 /** Determines whether or not this exception is caused by some type, directly or indirectly. */
-internal fun <T : Throwable> Throwable.isCausedBy(clazz: KClass<T>): Boolean {
+private fun <T : Throwable> Throwable.isCausedBy(clazz: KClass<T>): Boolean {
     if (clazz.isInstance(this)) {
         return true
     }
     return this.cause?.isCausedBy(clazz) == true
 }
 
-internal fun Throwable.toErrorStatusInternal(): ErrorStatusInternal {
+/**
+ * Returns an ErrorStatusInternal corresponding to this Throwable.
+ */
+private fun Throwable.toErrorStatusInternal(): ErrorStatusInternal {
     return when {
         this.isCausedBy(
             ExternalException::class
@@ -63,8 +70,40 @@
             StructConversionException::class
         ) -> ErrorStatusInternal.STRUCT_CONVERSION_FAILURE
         this.isCausedBy(
+            InvalidResolverException::class
+        ) -> ErrorStatusInternal.INVALID_RESOLVER
+        this.isCausedBy(
+            DisambigStateException::class
+        ) -> ErrorStatusInternal.UNCHANGED_DISAMBIG_STATE
+        this.isCausedBy(
             InvalidRequestException::class
         ) -> ErrorStatusInternal.INVALID_REQUEST
         else -> ErrorStatusInternal.CANCELLED
     }
+}
+
+/**
+ * Handles an exception encountered during request proessing (one-shot or multi-turn).
+ * Includes reporting an ErrorStatusInternal to some callback based on the exception.
+ */
+internal fun handleExceptionFromRequestProcessing(
+    t: Throwable,
+    errorCallback: (ErrorStatusInternal) -> Unit
+) {
+    LoggerInternal.log(
+        CapabilityLogger.LogLevel.ERROR,
+        LOG_TAG,
+        "exception encountered during request processing, this exception.",
+        t
+    )
+    errorCallback.invoke(t.toErrorStatusInternal())
+    if (!t.isCausedBy(InvalidRequestException::class)) {
+        LoggerInternal.log(
+            CapabilityLogger.LogLevel.ERROR,
+            LOG_TAG,
+            "Rethrowing exception because it is not caused by InvalidRequestException.",
+            t
+        )
+        throw t
+    }
 }
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
index a1513d9..322f1d7 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
@@ -42,7 +42,6 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.proto.TaskInfo
 import com.google.common.truth.Truth.assertThat
-import java.util.Optional
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeoutOrNull
@@ -123,7 +122,7 @@
             ExecutionCallback<Arguments, Output> {
                 ExecutionResult.Builder<Output>()
                     .setOutput(
-                        Output.builder().setOptionalStringField("stringOutput").build()
+                        Output.Builder().setOptionalStringField("stringOutput").build()
                     )
                     .build()
             }
@@ -305,7 +304,7 @@
 
         // verify ExecutionCallback receives 1st request.
         assertThat(argumentChannel.receive()).isEqualTo(
-            Arguments.newBuilder().setOptionalStringField("string value 1").build()
+            Arguments.Builder().setOptionalStringField("string value 1").build()
         )
         // verify the 2nd request cannot be received due to mutex.
         assertThat(withTimeoutOrNull(BLOCKING_TIMEOUT) { argumentChannel.receive() }).isNull()
@@ -317,7 +316,7 @@
         )
 
         assertThat(argumentChannel.receive()).isEqualTo(
-            Arguments.newBuilder().setOptionalStringField("string value 2").build()
+            Arguments.Builder().setOptionalStringField("string value 2").build()
         )
         executionResultChannel.send(ExecutionResult.Builder<Output>().build())
         assertThat(callbackInternal2.receiveResponse().fulfillmentResponse).isEqualTo(
@@ -327,31 +326,26 @@
 
     companion object {
         val ACTION_SPEC: ActionSpec<Arguments, Output> =
-            ActionSpecBuilder.ofCapabilityNamed(
-                "actions.intent.TEST"
-            )
-                .setArguments(Arguments::class.java, Arguments::newBuilder)
+            ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
+                .setArguments(Arguments::class.java, Arguments::Builder)
                 .setOutput(Output::class.java)
                 .bindParameter(
                     "requiredString",
-                    { properties -> properties["requiredEntity"] as Property<StringValue>? },
+                    { properties -> properties["requiredEntity"] as Property<StringValue> },
                     Arguments.Builder::setRequiredStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER
                 )
-                .bindOptionalParameter(
+                .bindParameter(
                     "optionalString",
                     { properties ->
-                        properties["optionalString"]
-                            ?.let { it as Property<StringValue> }
-                            ?.let { Optional.of(it) }
-                            ?: Optional.ofNullable(null)
+                        properties["optionalString"] as? Property<StringValue>
                     },
                     Arguments.Builder::setOptionalStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER
                 )
-                .bindOptionalOutput(
+                .bindOutput(
                     "optionalStringOutput",
                     Output::optionalStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER::toParamValue
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java
index a6615bf..291bcb0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java
@@ -24,6 +24,7 @@
 import androidx.appactions.builtintypes.experimental.types.Thing;
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
 import androidx.appactions.interaction.capabilities.core.testing.spec.TestEntity;
+import androidx.appactions.interaction.capabilities.core.testing.spec.TestEnum;
 import androidx.appactions.interaction.protobuf.Struct;
 import androidx.appactions.interaction.protobuf.Value;
 
@@ -45,27 +46,29 @@
     @Test
     public void bindIdentifier_success() {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindIdentifier(TestEntity::getId)
                         .build();
         assertThat(
                         entityTypeSpec.getIdentifier(
-                                TestEntity.newBuilder().setId("identifier1").build()))
+                                new TestEntity.Builder().setId("identifier1").build()))
                 .isEqualTo("identifier1");
-        assertThat(entityTypeSpec.getIdentifier(TestEntity.newBuilder().build())).isNull();
+        assertThat(entityTypeSpec.getIdentifier(new TestEntity.Builder().build())).isNull();
     }
 
     @Test
     public void bindEnumField_convertsSuccessfully() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindEnumField(
                                 "enum",
                                 TestEntity::getEnum,
                                 TestEntity.Builder::setEnum,
-                                TestEntity.TestEnum.class)
+                                TestEnum.class)
                         .build();
-        TestEntity entity = TestEntity.newBuilder().setEnum(TestEntity.TestEnum.VALUE_1).build();
+        TestEntity entity = new TestEntity.Builder().setEnum(TestEnum.VALUE_1).build();
         Value entityValue =
                 structToValue(
                         Struct.newBuilder()
@@ -74,7 +77,7 @@
                                         Value.newBuilder().setStringValue("TestEntity").build())
                                 .putFields(
                                         "enum",
-                                        Value.newBuilder().setStringValue("value_1").build())
+                                        Value.newBuilder().setStringValue("VALUE_1").build())
                                 .build());
 
         assertThat(entityTypeSpec.toValue(entity)).isEqualTo(entityValue);
@@ -84,12 +87,13 @@
     @Test
     public void bindEnumField_throwsException() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindEnumField(
                                 "enum",
                                 TestEntity::getEnum,
                                 TestEntity.Builder::setEnum,
-                                TestEntity.TestEnum.class)
+                                TestEnum.class)
                         .build();
         Value malformedValue =
                 structToValue(
@@ -109,13 +113,14 @@
     @Test
     public void bindDurationField_convertsSuccessfully() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindDurationField(
                                 "duration",
                                 TestEntity::getDuration,
                                 TestEntity.Builder::setDuration)
                         .build();
-        TestEntity entity = TestEntity.newBuilder().setDuration(Duration.ofMinutes(5)).build();
+        TestEntity entity = new TestEntity.Builder().setDuration(Duration.ofMinutes(5)).build();
         Value entityValue =
                 structToValue(
                         Struct.newBuilder()
@@ -134,14 +139,15 @@
     @Test
     public void bindZonedDateTimeField_convertsSuccessfully() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindZonedDateTimeField(
                                 "date",
                                 TestEntity::getZonedDateTime,
                                 TestEntity.Builder::setZonedDateTime)
                         .build();
         TestEntity entity =
-                TestEntity.newBuilder()
+                new TestEntity.Builder()
                         .setZonedDateTime(ZonedDateTime.of(2022, 1, 1, 8, 0, 0, 0, ZoneOffset.UTC))
                         .build();
         Value entityValue =
@@ -164,14 +170,15 @@
     @Test
     public void bindZonedDateTimeField_zoneId_convertsSuccessfully() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindZonedDateTimeField(
                                 "date",
                                 TestEntity::getZonedDateTime,
                                 TestEntity.Builder::setZonedDateTime)
                         .build();
         TestEntity entity =
-                TestEntity.newBuilder()
+                new TestEntity.Builder()
                         .setZonedDateTime(
                                 ZonedDateTime.of(2022, 1, 1, 8, 0, 0, 0, ZoneId.of("UTC+01:00")))
                         .build();
@@ -188,7 +195,7 @@
                                                 .build())
                                 .build());
         TestEntity expectedEntity =
-                TestEntity.newBuilder()
+                new TestEntity.Builder()
                         .setZonedDateTime(
                                 ZonedDateTime.of(2022, 1, 1, 8, 0, 0, 0, ZoneOffset.of("+01:00")))
                         .build();
@@ -200,7 +207,8 @@
     @Test
     public void bindZonedDateTimeField_throwsException() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindZonedDateTimeField(
                                 "date",
                                 TestEntity::getZonedDateTime,
@@ -224,12 +232,16 @@
     @Test
     public void bindSpecField_convertsSuccessfully() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindSpecField(
                                 "entity",
                                 TestEntity::getEntity,
                                 TestEntity.Builder::setEntity,
-                                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                                TypeSpecBuilder.newBuilder(
+                                                "TestEntity",
+                                                TestEntity.Builder::new,
+                                                TestEntity.Builder::build)
                                         .bindStringField(
                                                 "name",
                                                 TestEntity::getName,
@@ -237,8 +249,8 @@
                                         .build())
                         .build();
         TestEntity entity =
-                TestEntity.newBuilder()
-                        .setEntity(TestEntity.newBuilder().setName("entity name").build())
+                new TestEntity.Builder()
+                        .setEntity(new TestEntity.Builder().setName("entity name").build())
                         .build();
         Value entityValue = structToValue(
                 Struct.newBuilder()
@@ -274,12 +286,16 @@
     @Test
     public void bindSpecField_throwsException() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
-                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                TypeSpecBuilder.newBuilder(
+                                "TestEntity", TestEntity.Builder::new, TestEntity.Builder::build)
                         .bindSpecField(
                                 "entity",
                                 TestEntity::getEntity,
                                 TestEntity.Builder::setEntity,
-                                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                                TypeSpecBuilder.newBuilder(
+                                                "TestEntity",
+                                                TestEntity.Builder::new,
+                                                TestEntity.Builder::build)
                                         .bindStringField(
                                                 "name",
                                                 TestEntity::getName,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
index 6b7243a..899825f 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
@@ -19,20 +19,22 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
 import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.properties.Property;
 import androidx.appactions.interaction.capabilities.core.properties.StringValue;
+import androidx.appactions.interaction.capabilities.core.testing.spec.Arguments;
+import androidx.appactions.interaction.capabilities.core.testing.spec.GenericEntityArguments;
 import androidx.appactions.interaction.capabilities.core.testing.spec.Output;
+import androidx.appactions.interaction.capabilities.core.testing.spec.TestEntity;
 import androidx.appactions.interaction.proto.AppActionsContext.AppAction;
 import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput;
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput.OutputValue;
 import androidx.appactions.interaction.proto.ParamValue;
-
-import com.google.auto.value.AutoValue;
+import androidx.appactions.interaction.protobuf.Struct;
+import androidx.appactions.interaction.protobuf.Value;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,32 +43,31 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 
 
 @RunWith(JUnit4.class)
 @SuppressWarnings("unchecked")
 public final class ActionSpecTest {
     private static final ActionSpec<Arguments, Output> ACTION_SPEC =
-            ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
-                    .setArguments(Arguments.class, Arguments::newBuilder)
+            ActionSpecBuilder.Companion.ofCapabilityNamed("actions.intent.TEST")
+                    .setArguments(Arguments.class, Arguments.Builder::new)
                     .setOutput(Output.class)
                     .bindParameter(
                             "requiredString",
                             properties ->
                             {
-                                return (Property<StringValue>) (properties.get(
-                                        "requiredString"));
+                                Property<?> property = properties.get("requiredString");
+                                return (property == null) ? null : (Property<StringValue>) property;
                             },
                             Arguments.Builder::setRequiredStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
-                    .bindOptionalParameter(
+                    .bindParameter(
                             "optionalString",
                             properties ->
                             {
-                                return Optional.ofNullable((Property<StringValue>) (properties.get(
-                                        "optionalString")));
+                                Property<?> property = properties.get("optionalString");
+                                return (property == null) ? null : (Property<StringValue>) property;
                             },
                             Arguments.Builder::setOptionalStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
@@ -75,95 +76,108 @@
                             "repeatedString",
                             properties ->
                             {
-                                return Optional.ofNullable((Property<StringValue>) (properties.get(
-                                        "repeatedString")));
+                                Property<?> property = properties.get("repeatedString");
+                                return (property == null) ? null : (Property<StringValue>) property;
                             },
                             Arguments.Builder::setRepeatedStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                             TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
-                    .bindOptionalOutput(
+                    .bindOutput(
                             "optionalStringOutput",
-                            Output::optionalStringField,
+                            Output::getOptionalStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER::toParamValue)
                     .bindRepeatedOutput(
                             "repeatedStringOutput",
-                            Output::repeatedStringField,
+                            Output::getRepeatedStringField,
                             TypeConverters.STRING_PARAM_VALUE_CONVERTER::toParamValue)
                     .build();
-    private static final ParamValueConverter<String> STRING_PARAM_VALUE_CONVERTER =
-            new ParamValueConverter<String>() {
+    private static final ParamValueConverter<TestEntity> TEST_ENTITY_PARAM_VALUE_CONVERTER =
+            new ParamValueConverter<TestEntity>() {
                 @NonNull
                 @Override
-                public ParamValue toParamValue(String type) {
-                    return ParamValue.newBuilder().setStringValue(type).build();
+                public ParamValue toParamValue(TestEntity type) {
+                    return ParamValue.newBuilder()
+                            .setStructValue(
+                                    Struct.newBuilder()
+                                            .putFields(
+                                                    "name",
+                                                    Value.newBuilder()
+                                                            .setStringValue(type.getName())
+                                                            .build())
+                                            .build())
+                            .build();
                 }
 
                 @Override
-                public String fromParamValue(@NonNull ParamValue paramValue) {
-                    return "test";
+                public TestEntity fromParamValue(@NonNull ParamValue paramValue) {
+                    String name =
+                            paramValue.getStructValue().getFieldsOrThrow("name").getStringValue();
+                    return new TestEntity.Builder().setName(name).build();
                 }
             };
-    private static final EntityConverter<String> STRING_ENTITY_CONVERTER =
-            (theString) ->
+    private static final EntityConverter<TestEntity> TEST_ENTITY_CONVERTER =
+            (testEntity) ->
                     androidx.appactions.interaction.proto.Entity.newBuilder()
-                            .setIdentifier(theString)
-                            .setName(theString)
+                            .setIdentifier(testEntity.getId())
+                            .setName(testEntity.getName())
                             .build();
 
-    private static final ActionSpec<GenericEntityArguments, Output>
-            GENERIC_TYPES_ACTION_SPEC =
-            ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
-                    .setArguments(GenericEntityArguments.class,
-                            GenericEntityArguments::newBuilder)
+    private static final ActionSpec<GenericEntityArguments, Output> GENERIC_TYPES_ACTION_SPEC =
+            ActionSpecBuilder.Companion.ofCapabilityNamed("actions.intent.TEST")
+                    .setArguments(GenericEntityArguments.class, GenericEntityArguments.Builder::new)
                     .setOutput(Output.class)
                     .bindParameter(
                             "requiredEntity",
-                            properties ->
-                            {
-                                return (Property<String>) (properties.get(
-                                        "requiredEntity"));
+                            properties -> {
+                                Property<?> property = properties.get("requiredEntity");
+                                return (property == null) ? null : (Property<TestEntity>) property;
                             },
                             GenericEntityArguments.Builder::setSingularField,
-                            STRING_PARAM_VALUE_CONVERTER,
-                            STRING_ENTITY_CONVERTER)
-                    .bindOptionalParameter("optionalEntity",
-                            properties ->
-                            {
-                                return Optional.of((Property<String>) (properties.get(
-                                        "optionalEntity")));
+                            TEST_ENTITY_PARAM_VALUE_CONVERTER,
+                            TEST_ENTITY_CONVERTER)
+                    .bindParameter(
+                            "optionalEntity",
+                            properties -> {
+                                Property<?> property = properties.get("optionalEntity");
+                                return (property == null) ? null : (Property<TestEntity>) property;
                             },
                             GenericEntityArguments.Builder::setOptionalField,
-                            STRING_PARAM_VALUE_CONVERTER,
-                            STRING_ENTITY_CONVERTER)
-                    .bindRepeatedParameter("repeatedEntities",
-                            properties ->
-                            {
-                                return Optional.of((Property<String>) (properties.get(
-                                        "repeatedEntities")));
+                            TEST_ENTITY_PARAM_VALUE_CONVERTER,
+                            TEST_ENTITY_CONVERTER)
+                    .bindRepeatedParameter(
+                            "repeatedEntities",
+                            properties -> {
+                                Property<?> property = properties.get("repeatedEntities");
+                                return (property == null) ? null : (Property<TestEntity>) property;
                             },
                             GenericEntityArguments.Builder::setRepeatedField,
-                            STRING_PARAM_VALUE_CONVERTER,
-                            STRING_ENTITY_CONVERTER)
+                            TEST_ENTITY_PARAM_VALUE_CONVERTER,
+                            TEST_ENTITY_CONVERTER)
                     .build();
 
     @Test
     public void getAppAction_genericParameters() {
         Map<String, Property<?>> property = new HashMap<>();
-        property.put("requiredEntity",
-                new Property.Builder<String>()
+        property.put(
+                "requiredEntity",
+                new Property.Builder<TestEntity>()
                         .setRequired(true)
-                        .setPossibleValues("one")
+                        .setPossibleValues(
+                                new TestEntity.Builder().setId("one").setName("one").build())
                         .build());
-        property.put("optionalEntity",
-                new Property.Builder<String>()
+        property.put(
+                "optionalEntity",
+                new Property.Builder<TestEntity>()
                         .setRequired(true)
-                        .setPossibleValues("two")
-                        .build()
-        );
-        property.put("repeatedEntities",
-                new Property.Builder<String>()
+                        .setPossibleValues(
+                                new TestEntity.Builder().setId("two").setName("two").build())
+                        .build());
+        property.put(
+                "repeatedEntities",
+                new Property.Builder<TestEntity>()
                         .setRequired(true)
-                        .setPossibleValues("three")
+                        .setPossibleValues(
+                                new TestEntity.Builder().setId("three").setName("three").build())
                         .build());
 
         assertThat(GENERIC_TYPES_ACTION_SPEC.convertPropertyToProto(property))
@@ -264,7 +278,7 @@
     @Test
     public void convertOutputToProto_string() {
         Output output =
-                Output.builder()
+                new Output.Builder()
                         .setOptionalStringField("test2")
                         .setRepeatedStringField(List.of("test3", "test4"))
                         .build();
@@ -301,7 +315,8 @@
 
     @Test
     public void convertOutputToProto_emptyOutput() {
-        Output output = Output.builder().setRepeatedStringField(List.of("test3", "test4")).build();
+        Output output =
+                new Output.Builder().setRepeatedStringField(List.of("test3", "test4")).build();
         // No optionalStringOutput since it is not in the output above.
         StructuredOutput expectedExecutionOutput =
                 StructuredOutput.newBuilder()
@@ -324,116 +339,4 @@
         assertThat(executionOutput.getOutputValuesList())
                 .containsExactlyElementsIn(expectedExecutionOutput.getOutputValuesList());
     }
-
-    enum TestEnum {
-        VALUE_1,
-        VALUE_2,
-    }
-
-    @AutoValue
-    abstract static class Arguments {
-
-        static Builder newBuilder() {
-            return new AutoValue_ActionSpecTest_Arguments.Builder();
-        }
-
-        abstract String requiredStringField();
-
-        abstract String optionalStringField();
-
-        abstract List<String> repeatedStringField();
-
-        @AutoValue.Builder
-        abstract static class Builder implements BuilderOf<Arguments> {
-
-            abstract Builder setRequiredStringField(String value);
-
-            abstract Builder setOptionalStringField(String value);
-
-            abstract Builder setRepeatedStringField(List<String> repeated);
-
-            @NonNull
-            @Override
-            public abstract Arguments build();
-        }
-    }
-
-    @AutoValue
-    abstract static class Properties {
-
-        static Properties create(
-                Optional<Property<TestEnum>> optionalEnumField,
-                Property<StringValue> requiredStringField,
-                Optional<Property<StringValue>> optionalStringField,
-                Optional<Property<StringValue>> repeatedStringField) {
-            return new AutoValue_ActionSpecTest_Properties(
-                    optionalEnumField,
-                    requiredStringField,
-                    optionalStringField,
-                    repeatedStringField);
-        }
-
-        static Properties create(
-                Property<StringValue> requiredStringField) {
-            return create(
-                    Optional.empty(),
-                    requiredStringField,
-                    Optional.empty(),
-                    Optional.empty());
-        }
-
-        abstract Optional<Property<TestEnum>> optionalEnumField();
-
-        abstract Property<StringValue> requiredStringField();
-
-        abstract Optional<Property<StringValue>> optionalStringField();
-
-        abstract Optional<Property<StringValue>> repeatedStringField();
-    }
-
-    @AutoValue
-    abstract static class GenericEntityArguments {
-
-        static Builder newBuilder() {
-            return new AutoValue_ActionSpecTest_GenericEntityArguments.Builder();
-        }
-
-        abstract String singularField();
-
-        abstract String optionalField();
-
-        abstract List<String> repeatedField();
-
-        @AutoValue.Builder
-        abstract static class Builder implements BuilderOf<GenericEntityArguments> {
-
-            abstract Builder setSingularField(String value);
-
-            abstract Builder setOptionalField(String value);
-
-            abstract Builder setRepeatedField(List<String> value);
-
-            @NonNull
-            @Override
-            public abstract GenericEntityArguments build();
-        }
-    }
-
-    @AutoValue
-    abstract static class GenericEntityProperty {
-
-        static GenericEntityProperty create(
-                Property<String> singularField,
-                Optional<Property<String>> optionalField,
-                Optional<Property<String>> repeatedField) {
-            return new AutoValue_ActionSpecTest_GenericEntityProperty(
-                    singularField, optionalField, repeatedField);
-        }
-
-        abstract Property<String> singularField();
-
-        abstract Optional<Property<String>> optionalField();
-
-        abstract Optional<Property<String>> repeatedField();
-    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
index 18c1cab..241d5f3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
@@ -70,7 +70,6 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.ListenableFuture
-import java.util.Optional
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.atomic.AtomicReference
 import java.util.function.Supplier
@@ -93,7 +92,7 @@
                         Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
                 }
             },
-            sessionBridge = { TaskHandler.Builder<Confirmation>().build() },
+            sessionBridge = { TaskHandler.Builder<Arguments, Confirmation>().build() },
             sessionUpdaterSupplier = ::EmptyTaskUpdater
         )
     private val hostProperties: HostProperties =
@@ -139,7 +138,7 @@
                         Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
                 }
             },
-            sessionBridge = { TaskHandler.Builder<Confirmation>().build() },
+            sessionBridge = { TaskHandler.Builder<Arguments, Confirmation>().build() },
             sessionUpdaterSupplier = ::EmptyTaskUpdater
         )
         mutableEntityList.add(StringValue.of("entity1"))
@@ -186,7 +185,7 @@
             createCapability(
                 SINGLE_REQUIRED_FIELD_PROPERTY,
                 { externalSession },
-                { TaskHandler.Builder<Confirmation>().build() },
+                { TaskHandler.Builder<Arguments, Confirmation>().build() },
                 ::EmptyTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
@@ -201,7 +200,8 @@
             createCapability(
                 SINGLE_REQUIRED_FIELD_PROPERTY,
                 sessionFactory =
-                { _ -> object : ExecutionSession {
+                { _ ->
+                    object : ExecutionSession {
                         override fun onCreate(sessionConfig: SessionConfig) {
                             onCreateInvocationCount.incrementAndGet()
                         }
@@ -210,8 +210,11 @@
                             Futures.immediateFuture(
                                 ExecutionResult.Builder<Output>().build()
                             )
-                    } },
-                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                    }
+                },
+                sessionBridge = SessionBridge {
+                    TaskHandler.Builder<Arguments, Confirmation>().build()
+                },
                 sessionUpdaterSupplier = ::EmptyTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
@@ -288,7 +291,9 @@
         val capability: Capability = createCapability(
             SINGLE_REQUIRED_FIELD_PROPERTY,
             sessionFactory = { _ -> externalSession },
-            sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+            sessionBridge = SessionBridge {
+                TaskHandler.Builder<Arguments, Confirmation>().build()
+            },
             sessionUpdaterSupplier = ::RequiredTaskUpdater
         )
         val session = capability.createSession("mySessionId", hostProperties)
@@ -326,7 +331,9 @@
                             )
                     }
                 },
-                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                sessionBridge = SessionBridge {
+                    TaskHandler.Builder<Arguments, Confirmation>().build()
+                },
                 sessionUpdaterSupplier = ::RequiredTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
@@ -360,20 +367,27 @@
                 .build(),
             "stringSlotB" to Property.Builder<StringValue>()
                 .setRequired(true)
-                .build(),
+                .build()
         )
         val sessionFactory:
-                (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
+            (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
             { _ ->
                 object : CapabilityTwoStrings.ExecutionSession {
                     override suspend fun onExecute(
                         arguments: CapabilityTwoStrings.Arguments
-                    ): ExecutionResult<Void> = ExecutionResult.Builder<Void>().build()
+                    ): ExecutionResult<CapabilityTwoStrings.Output> =
+                        ExecutionResult.Builder<CapabilityTwoStrings.Output>().build()
                 }
             }
         val sessionBridge =
-            SessionBridge<CapabilityTwoStrings.ExecutionSession, Void> {
-                TaskHandler.Builder<Void>()
+            SessionBridge<
+                CapabilityTwoStrings.ExecutionSession,
+                CapabilityTwoStrings.Arguments,
+                CapabilityTwoStrings.Confirmation> {
+                TaskHandler.Builder<
+                    CapabilityTwoStrings.Arguments,
+                    CapabilityTwoStrings.Confirmation
+                >()
                     .registerValueTaskParam(
                         "stringSlotA",
                         AUTO_ACCEPT_STRING_VALUE,
@@ -446,23 +460,29 @@
                 .build(),
             "stringSlotB" to Property.Builder<StringValue>()
                 .setRequired(false)
-                .build(),
+                .build()
         )
         val sessionFactory:
-                (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
+            (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
             { _ ->
                 object : CapabilityTwoStrings.ExecutionSession {
                     override suspend fun onExecute(
                         arguments: CapabilityTwoStrings.Arguments
-                    ): ExecutionResult<Void> {
+                    ): ExecutionResult<CapabilityTwoStrings.Output> {
                         onExecuteInvocationCount.incrementAndGet()
-                        return ExecutionResult.Builder<Void>().build()
+                        return ExecutionResult.Builder<CapabilityTwoStrings.Output>().build()
                     }
                 }
             }
         val sessionBridge =
-            SessionBridge<CapabilityTwoStrings.ExecutionSession, Void> {
-                TaskHandler.Builder<Void>()
+            SessionBridge<
+                CapabilityTwoStrings.ExecutionSession,
+                CapabilityTwoStrings.Arguments,
+                CapabilityTwoStrings.Confirmation> {
+                TaskHandler.Builder<
+                    CapabilityTwoStrings.Arguments,
+                    CapabilityTwoStrings.Confirmation
+                >()
                     .registerValueTaskParam(
                         "stringSlotA",
                         AUTO_ACCEPT_STRING_VALUE,
@@ -531,13 +551,15 @@
             "optionalEnum" to Property.Builder<TestEnum>()
                 .setPossibleValues(TestEnum.VALUE_1, TestEnum.VALUE_2)
                 .setRequired(true)
-                .build(),
+                .build()
         )
         val capability: Capability =
             createCapability(
                 property,
                 sessionFactory = { _ -> ExecutionSession.DEFAULT },
-                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                sessionBridge = SessionBridge {
+                    TaskHandler.Builder<Arguments, Confirmation>().build()
+                },
                 sessionUpdaterSupplier = ::EmptyTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
@@ -593,7 +615,7 @@
                         override suspend fun onExecute(arguments: Arguments) =
                             ExecutionResult.Builder<Output>().build()
 
-                        override fun getRequiredStringListener() =
+                        override val requiredStringListener =
                             object : AppEntityListener<String> {
                                 override fun lookupAndRenderAsync(
                                     searchAction: SearchAction<String>
@@ -615,18 +637,18 @@
                     }
                 },
                 sessionBridge =
-                SessionBridge<ExecutionSession, Confirmation> { session ->
-                    val builder = TaskHandler.Builder<Confirmation>()
-                    session.getRequiredStringListener()
-                        ?.let { listener: AppEntityListener<String> ->
-                            builder.registerAppEntityTaskParam(
-                                "required",
-                                listener,
-                                TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-                                EntityConverter.of(TypeSpec.STRING_TYPE_SPEC),
-                                getTrivialSearchActionConverter()
-                            )
-                        }
+                SessionBridge<ExecutionSession, Arguments, Confirmation> { session ->
+                    val builder = TaskHandler.Builder<Arguments, Confirmation>()
+                    session.requiredStringListener?.let {
+                            listener: AppEntityListener<String> ->
+                        builder.registerAppEntityTaskParam(
+                            "required",
+                            listener,
+                            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                            EntityConverter.of(TypeSpec.STRING_TYPE_SPEC),
+                            getTrivialSearchActionConverter()
+                        )
+                    }
                     builder.build()
                 },
                 sessionUpdaterSupplier = ::EmptyTaskUpdater
@@ -720,13 +742,13 @@
     fun identifierOnly_refillsStruct() = runBlocking<Unit> {
         val property = mapOf(
             "listItem" to Property.Builder<
-                ListItem,
+                ListItem
                 >()
                 .setRequired(true)
                 .build(),
             "anyString" to Property.Builder<StringValue>()
                 .setRequired(true)
-                .build(),
+                .build()
         )
         val item1: ListItem = ListItem.Builder().setName("red apple").setIdentifier("item1").build()
         val item2: ListItem =
@@ -736,20 +758,18 @@
         val onExecuteStringDeferred = CompletableDeferred<String>()
 
         val sessionFactory:
-                (hostProperties: HostProperties?) -> CapabilityStructFill.ExecutionSession =
+            (hostProperties: HostProperties?) -> CapabilityStructFill.ExecutionSession =
             { _ ->
                 object : CapabilityStructFill.ExecutionSession {
                     override suspend fun onExecute(
                         arguments: CapabilityStructFill.Arguments
-                    ): ExecutionResult<Void> {
-                        val listItem: ListItem = arguments.listItem().orElse(null)
-                        val string: String = arguments.anyString().orElse(null)
-                        onExecuteListItemDeferred.complete(listItem)
-                        onExecuteStringDeferred.complete(string)
-                        return ExecutionResult.Builder<Void>().build()
+                    ): ExecutionResult<CapabilityStructFill.Output> {
+                        arguments.listItem?.let { onExecuteListItemDeferred.complete(it) }
+                        arguments.anyString?.let { onExecuteStringDeferred.complete(it) }
+                        return ExecutionResult.Builder<CapabilityStructFill.Output>().build()
                     }
 
-                    override fun getListItemListener() =
+                    override val listItemListener =
                         object : AppEntityListener<ListItem> {
                             override fun onReceivedAsync(
                                 value: ListItem
@@ -771,8 +791,14 @@
                 }
             }
         val sessionBridge =
-            SessionBridge<CapabilityStructFill.ExecutionSession, Void> { session ->
-                TaskHandler.Builder<Void>()
+            SessionBridge<
+                CapabilityStructFill.ExecutionSession,
+                CapabilityStructFill.Arguments,
+                CapabilityStructFill.Confirmation> { session ->
+                TaskHandler.Builder<
+                    CapabilityStructFill.Arguments,
+                    CapabilityStructFill.Confirmation
+                >()
                     .registerAppEntityTaskParam(
                         "listItem",
                         session.listItemListener,
@@ -878,7 +904,7 @@
                     override suspend fun onExecute(arguments: Arguments) =
                         ExecutionResult.Builder<Output>()
                             .setOutput(
-                                Output.builder()
+                                Output.Builder()
                                     .setOptionalStringField("bar")
                                     .setRepeatedStringField(
                                         listOf("bar1", "bar2")
@@ -977,8 +1003,10 @@
             createCapability(
                 property,
                 sessionFactory = sessionFactory,
-                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-                sessionUpdaterSupplier = ::EmptyTaskUpdater,
+                sessionBridge = SessionBridge {
+                    TaskHandler.Builder<Arguments, Confirmation>().build()
+                },
+                sessionUpdaterSupplier = ::EmptyTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
 
@@ -986,7 +1014,7 @@
         val callback = FakeCallbackInternal()
         session.execute(
             buildRequestArgs(SYNC),
-            callback,
+            callback
         )
         assertThat(callback.receiveResponse()).isNotNull()
         assertThat(getCurrentValues("required", session.state!!)).isEmpty()
@@ -995,11 +1023,12 @@
         // TURN 2. Providing the required slots so that the task completes and the state gets cleared
         val callback2 = FakeCallbackInternal()
         session.execute(
-            buildRequestArgs(SYNC,
+            buildRequestArgs(
+                SYNC,
                 "required",
                 ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
             ),
-            callback2,
+            callback2
         )
         assertThat(callback2.receiveResponse().fulfillmentResponse).isNotNull()
         assertThat(session.isActive).isEqualTo(false)
@@ -1017,13 +1046,15 @@
                 }
             }
         var onReadyToConfirm =
-             object : OnReadyToConfirmListenerInternal<Confirmation> {
-                override suspend fun onReadyToConfirm(args: Map<String, List<ParamValue>>):
+            object : OnReadyToConfirmListener<Arguments, Confirmation> {
+                override suspend fun onReadyToConfirm(arguments: Arguments):
                     ConfirmationOutput<Confirmation> {
                     return ConfirmationOutput.Builder<Confirmation>()
-                            .setConfirmation(Confirmation.builder().setOptionalStringField("bar")
-                                .build())
-                            .build()
+                        .setConfirmation(
+                            Confirmation.Builder().setOptionalStringField("bar")
+                                .build()
+                        )
+                        .build()
                 }
             }
         val property = mapOf(
@@ -1034,21 +1065,23 @@
                 property,
                 sessionFactory = sessionFactory,
                 sessionBridge = SessionBridge {
-                                    TaskHandler.Builder<Confirmation>()
-                                        .setOnReadyToConfirmListenerInternal(onReadyToConfirm)
-                                        .build() },
-                sessionUpdaterSupplier = ::EmptyTaskUpdater,
+                    TaskHandler.Builder<Arguments, Confirmation>()
+                        .setOnReadyToConfirmListener(onReadyToConfirm)
+                        .build()
+                },
+                sessionUpdaterSupplier = ::EmptyTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
 
         // TURN 1. Providing all the required slots in the SYNC Request
         val callback = FakeCallbackInternal()
         session.execute(
-            buildRequestArgs(SYNC,
+            buildRequestArgs(
+                SYNC,
                 "required",
                 ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
             ),
-            callback,
+            callback
         )
         assertThat(callback.receiveResponse()).isNotNull()
         assertThat(session.isActive).isEqualTo(true)
@@ -1081,19 +1114,22 @@
             createCapability(
                 property,
                 sessionFactory = sessionFactory,
-                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-                sessionUpdaterSupplier = ::EmptyTaskUpdater,
+                sessionBridge = SessionBridge {
+                    TaskHandler.Builder<Arguments, Confirmation>().build()
+                },
+                sessionUpdaterSupplier = ::EmptyTaskUpdater
             )
         val session = capability.createSession(fakeSessionId, hostProperties)
 
         // TURN 1. Providing the required slots so that the task completes and the state gets cleared
         val callback = FakeCallbackInternal()
         session.execute(
-            buildRequestArgs(SYNC,
+            buildRequestArgs(
+                SYNC,
                 "required",
                 ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
             ),
-            callback,
+            callback
         )
         assertThat(callback.receiveResponse().fulfillmentResponse).isNotNull()
         assertThat(session.isActive).isEqualTo(false)
@@ -1101,17 +1137,111 @@
         // TURN 2. Trying to sync after the session is destroyed
         val callback2 = FakeCallbackInternal()
         session.execute(
-            buildRequestArgs(SYNC,
+            buildRequestArgs(
+                SYNC,
                 "required",
                 ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
             ),
-            callback2,
+            callback2
         )
         assertThat(session.isActive).isEqualTo(false)
         assertThat(callback2.receiveResponse().errorStatus)
             .isEqualTo(ErrorStatusInternal.SESSION_ALREADY_DESTROYED)
     }
 
+    @Test
+    fun structConversionException_shouldReportStructConversionFailure() {
+        val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession =
+            { _ ->
+                object : ExecutionSession {
+                    override val requiredStringListener = object : AppEntityListener<String> {
+                        override fun lookupAndRenderAsync(
+                            searchAction: SearchAction<String>
+                        ): ListenableFuture<EntitySearchResult<String>> {
+                            return Futures.immediateFuture(
+                                EntitySearchResult.Builder<String>().build()
+                            )
+                        }
+
+                        override fun onReceivedAsync(
+                            value: String
+                        ): ListenableFuture<ValidationResult> {
+                            return Futures.immediateFuture(ValidationResult.newAccepted())
+                        }
+                    }
+                    override suspend fun onExecute(arguments: Arguments) =
+                        ExecutionResult.Builder<Output>()
+                            .setShouldStartDictation(true)
+                            .build()
+                }
+            }
+        val capability = CapabilityBuilder()
+            .setId("fakeId")
+            .setExecutionSessionFactory(sessionFactory)
+            .build()
+        val session = capability.createSession(fakeSessionId, hostProperties)
+        val callback = FakeCallbackInternal()
+
+        session.execute(
+            buildRequestArgs(
+                SYNC,
+                "required",
+                // this ParamValue can't be parsed as String, and causes StructConversionException
+                ParamValue.newBuilder().setIdentifier("foo").setBoolValue(false).build()
+            ),
+            callback
+        )
+
+        assertThat(
+            callback.receiveResponse().errorStatus
+        ).isEqualTo(ErrorStatusInternal.STRUCT_CONVERSION_FAILURE)
+    }
+
+    @Test
+    fun slotListenerException_shouldReportExternalException() {
+        val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession =
+            { _ ->
+                object : ExecutionSession {
+                    override val requiredStringListener = object : AppEntityListener<String> {
+                        override fun lookupAndRenderAsync(
+                            searchAction: SearchAction<String>
+                        ): ListenableFuture<EntitySearchResult<String>> {
+                            throw IllegalStateException("error in lookupAndRender")
+                        }
+
+                        override fun onReceivedAsync(
+                            value: String
+                        ): ListenableFuture<ValidationResult> {
+                            throw IllegalStateException("error in onReceivedAsync")
+                        }
+                    }
+                    override suspend fun onExecute(arguments: Arguments) =
+                        ExecutionResult.Builder<Output>()
+                            .setShouldStartDictation(true)
+                            .build()
+                }
+            }
+        val capability = CapabilityBuilder()
+            .setId("fakeId")
+            .setExecutionSessionFactory(sessionFactory)
+            .build()
+        val session = capability.createSession(fakeSessionId, hostProperties)
+        val callback = FakeCallbackInternal()
+
+        session.execute(
+            buildRequestArgs(
+                SYNC, /* args...= */
+                "required",
+                ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
+            ),
+            callback
+        )
+
+        assertThat(
+            callback.receiveResponse().errorStatus
+        ).isEqualTo(ErrorStatusInternal.EXTERNAL_EXCEPTION)
+    }
+
     /**
      * an implementation of Capability.Builder using Argument. Output, etc. defined under
      * testing/spec
@@ -1129,8 +1259,24 @@
             setProperty(SINGLE_REQUIRED_FIELD_PROPERTY)
         }
 
-        override val sessionBridge: SessionBridge<ExecutionSession, Confirmation> = SessionBridge {
-            TaskHandler.Builder<Confirmation>().build()
+        override val sessionBridge: SessionBridge<
+            ExecutionSession,
+            Arguments,
+            Confirmation
+        > = SessionBridge {
+                session ->
+            val builder = TaskHandler.Builder<Arguments, Confirmation>()
+            session.requiredStringListener?.let {
+                    listener: AppEntityListener<String> ->
+                builder.registerAppEntityTaskParam(
+                    "required",
+                    listener,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    EntityConverter.of(TypeSpec.STRING_TYPE_SPEC),
+                    getTrivialSearchActionConverter()
+                )
+            }
+            builder.build()
         }
     }
 
@@ -1192,38 +1338,30 @@
             ActionSpecBuilder.ofCapabilityNamed(
                 CAPABILITY_NAME
             )
-                .setArguments(Arguments::class.java, Arguments::newBuilder)
+                .setArguments(Arguments::class.java, Arguments::Builder)
                 .setOutput(Output::class.java)
                 .bindParameter(
                     "required",
                     { properties ->
-                        properties["required"]
-                            as
-                            Property<StringValue>?
+                        properties["required"] as Property<StringValue>
                     },
                     Arguments.Builder::setRequiredStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER
                 )
-                .bindOptionalParameter(
+                .bindParameter(
                     "optional",
                     { properties ->
-                        properties["optional"]
-                            ?.let { it as Property<StringValue> }
-                            ?.let { Optional.of(it) }
-                            ?: Optional.ofNullable(null)
+                        properties["optional"] as? Property<StringValue>
                     },
                     Arguments.Builder::setOptionalStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER
                 )
-                .bindOptionalParameter(
+                .bindParameter(
                     "optionalEnum",
                     { properties ->
-                        properties["optionalEnum"]
-                            ?.let { it as Property<TestEnum> }
-                            ?.let { Optional.of(it) }
-                            ?: Optional.ofNullable(null)
+                        properties["optionalEnum"] as? Property<TestEnum>
                     },
                     Arguments.Builder::setEnumField,
                     ENUM_CONVERTER,
@@ -1232,16 +1370,13 @@
                 .bindRepeatedParameter(
                     "repeated",
                     { properties ->
-                        properties["repeated"]
-                            ?.let { it as Property<StringValue> }
-                            ?.let { Optional.of(it) }
-                            ?: Optional.ofNullable(null)
+                        properties["repeated"] as? Property<StringValue>
                     },
                     Arguments.Builder::setRepeatedStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER,
                     TypeConverters.STRING_VALUE_ENTITY_CONVERTER
                 )
-                .bindOptionalOutput(
+                .bindOutput(
                     "optionalStringOutput",
                     Output::optionalStringField,
                     TypeConverters.STRING_PARAM_VALUE_CONVERTER::toParamValue
@@ -1279,7 +1414,7 @@
         private fun <SessionUpdaterT : AbstractTaskUpdater> createCapability(
             property: Map<String, Property<*>>,
             sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession,
-            sessionBridge: SessionBridge<ExecutionSession, Confirmation>,
+            sessionBridge: SessionBridge<ExecutionSession, Arguments, Confirmation>,
             sessionUpdaterSupplier: Supplier<SessionUpdaterT>
         ): TaskCapabilityImpl<
             Arguments,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Arguments.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Arguments.java
deleted file mode 100644
index 79532a1..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Arguments.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.List;
-import java.util.Optional;
-
-/** Testing implementation of a capability Argument. */
-@AutoValue
-public abstract class Arguments {
-
-    public static Builder newBuilder() {
-        return new AutoValue_Arguments.Builder();
-    }
-
-    public abstract Optional<String> requiredStringField();
-
-    public abstract Optional<String> optionalStringField();
-
-    public abstract Optional<TestEnum> enumField();
-
-    public abstract Optional<List<String>> repeatedStringField();
-
-    /** Builder for the testing Argument. */
-    @AutoValue.Builder
-    public abstract static class Builder implements BuilderOf<Arguments> {
-
-        public abstract Builder setRequiredStringField(String value);
-
-        public abstract Builder setOptionalStringField(String value);
-
-        public abstract Builder setEnumField(TestEnum value);
-
-        public abstract Builder setRepeatedStringField(List<String> value);
-
-        @NonNull
-        @Override
-        public abstract Arguments build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Arguments.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Arguments.kt
new file mode 100644
index 0000000..cc0a5e4
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Arguments.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+
+class Arguments internal constructor(
+    val requiredStringField: String?,
+    val optionalStringField: String?,
+    val enumField: TestEnum?,
+    val repeatedStringField: List<String>
+) {
+    override fun toString(): String {
+        return "Arguments(requiredStringField=$requiredStringField, " +
+            "optionalStringField=$optionalStringField, " +
+            "enumField=$enumField, " +
+            "repeatedStringField=$repeatedStringField, " +
+            ")"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as Arguments
+
+        if (requiredStringField != other.requiredStringField) return false
+        if (optionalStringField != other.optionalStringField) return false
+        if (enumField != other.enumField) return false
+        if (repeatedStringField != other.repeatedStringField) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = requiredStringField.hashCode()
+        result += 31 * optionalStringField.hashCode()
+        result += 31 * enumField.hashCode()
+        result += 31 * repeatedStringField.hashCode()
+        return result
+    }
+
+    class Builder : BuilderOf<Arguments> {
+        private var requiredStringField: String? = null
+        private var optionalStringField: String? = null
+        private var enumField: TestEnum? = null
+        private var repeatedStringField: List<String> = listOf()
+
+        fun setRequiredStringField(requiredStringField: String): Builder =
+            apply { this.requiredStringField = requiredStringField }
+
+        fun setOptionalStringField(optionalStringField: String): Builder =
+            apply { this.optionalStringField = optionalStringField }
+
+        fun setEnumField(enumField: TestEnum): Builder =
+            apply { this.enumField = enumField }
+
+        fun setRepeatedStringField(repeatedStringField: List<String>): Builder =
+            apply { this.repeatedStringField = repeatedStringField }
+
+        override fun build(): Arguments =
+            Arguments(requiredStringField, optionalStringField, enumField, repeatedStringField)
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
deleted file mode 100644
index 586145d..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import static androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.LIST_ITEM_TYPE_SPEC;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.builtintypes.experimental.types.ListItem;
-import androidx.appactions.interaction.capabilities.core.AppEntityListener;
-import androidx.appactions.interaction.capabilities.core.BaseExecutionSession;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter;
-import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
-import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
-import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
-import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.capabilities.core.properties.StringValue;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.Optional;
-
-/** Used to test the filling behavior of structured entities (e.g. ListItem) */
-@SuppressWarnings("unchecked")
-public final class CapabilityStructFill {
-
-    private static final String CAPABILITY_NAME = "actions.intent.TEST";
-    public static final ActionSpec<Arguments, Void> ACTION_SPEC =
-            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-                    .setArguments(Arguments.class, Arguments::newBuilder)
-                    .bindOptionalParameter(
-                            "listItem",
-                            properties ->
-                            {
-                                return Optional.ofNullable((Property<ListItem>) (properties.get(
-                                        "listItem")));
-                            },
-                            Arguments.Builder::setListItem,
-                            ParamValueConverter.Companion.of(LIST_ITEM_TYPE_SPEC),
-                            EntityConverter.Companion.of(LIST_ITEM_TYPE_SPEC)::convert)
-                    .bindOptionalParameter(
-                            "string",
-                            properties ->
-                            {
-                                return Optional.ofNullable((Property<StringValue>) (properties.get(
-                                        "anyString")));
-                            },
-                            Arguments.Builder::setAnyString,
-                            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-                            TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
-                    .build();
-
-    private CapabilityStructFill() {}
-
-    /** Two required strings */
-    @AutoValue
-    public abstract static class Arguments {
-        public static Builder newBuilder() {
-            return new AutoValue_CapabilityStructFill_Arguments.Builder();
-        }
-
-        public abstract Optional<ListItem> listItem();
-
-        public abstract Optional<String> anyString();
-
-        /** Builder for the testing Arguments. */
-        @AutoValue.Builder
-        public abstract static class Builder implements BuilderOf<Arguments> {
-
-            public abstract Builder setListItem(@NonNull ListItem value);
-
-            public abstract Builder setAnyString(@NonNull String value);
-
-            @NonNull
-            @Override
-            public abstract Arguments build();
-        }
-    }
-
-    /** Two required strings */
-    @AutoValue
-    public abstract static class Properties {
-        @NonNull
-        public static Builder newBuilder() {
-            return new AutoValue_CapabilityStructFill_Properties.Builder();
-        }
-
-        public abstract Optional<Property<ListItem>> listItem();
-
-        public abstract Optional<Property<StringValue>> anyString();
-
-        /** Builder for {@link Property} */
-        @AutoValue.Builder
-        public abstract static class Builder {
-
-            @NonNull
-            public abstract Builder setListItem(@NonNull Property<ListItem> value);
-
-            @NonNull
-            public abstract Builder setAnyString(@NonNull Property<StringValue> value);
-
-            @NonNull
-            public abstract Properties build();
-        }
-    }
-
-    public interface ExecutionSession extends BaseExecutionSession<Arguments, Void> {
-        @NonNull
-        AppEntityListener<ListItem> getListItemListener();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.kt
new file mode 100644
index 0000000..40d0f99
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+import androidx.appactions.builtintypes.experimental.types.ListItem
+import androidx.appactions.interaction.capabilities.core.AppEntityListener
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+
+private const val CAPABILITY_NAME = "actions.intent.TEST"
+
+/** Used to test the filling behavior of structured entities (e.g. ListItem)  */
+class CapabilityStructFill {
+
+    class Arguments internal constructor(
+        val listItem: ListItem?,
+        val anyString: String?
+    ) {
+        override fun toString(): String {
+            return "Arguments(listItem=$listItem, " +
+                "anyString=$anyString)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Arguments
+
+            if (listItem != other.listItem) return false
+            if (anyString != other.anyString) return false
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = listItem.hashCode()
+            result += 31 * anyString.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Arguments> {
+            private var listItem: ListItem? = null
+            private var anyString: String? = null
+
+            fun setListItem(listItem: ListItem): Builder =
+                apply { this.listItem = listItem }
+
+            fun setAnyString(stringSlotB: String): Builder =
+                apply { this.anyString = stringSlotB }
+
+            override fun build(): Arguments = Arguments(listItem, anyString)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    interface ExecutionSession : BaseExecutionSession<Arguments, Output> {
+        val listItemListener: AppEntityListener<ListItem>
+    }
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+            .setArguments(Arguments::class.java, Arguments::Builder)
+            .setOutput(Output::class.java)
+            .bindParameter(
+                "listItem",
+                { properties ->
+                    properties["listItem"] as? Property<ListItem>
+                },
+                Arguments.Builder::setListItem,
+                ParamValueConverter.of(TypeConverters.LIST_ITEM_TYPE_SPEC),
+                EntityConverter.of(TypeConverters.LIST_ITEM_TYPE_SPEC)::convert
+            )
+            .bindParameter(
+                "string",
+                { properties ->
+                    properties["anyString"] as? Property<StringValue>
+                },
+                Arguments.Builder::setAnyString,
+                TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+            )
+            .build()
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
deleted file mode 100644
index 76cf08d..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.BaseExecutionSession;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
-import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
-import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.capabilities.core.properties.StringValue;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.Optional;
-
-@SuppressWarnings("unchecked")
-public final class CapabilityTwoStrings {
-    private static final String CAPABILITY_NAME = "actions.intent.TEST";
-    public static final ActionSpec<Arguments, Void> ACTION_SPEC =
-            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-                    .setArguments(Arguments.class, Arguments::newBuilder)
-                    .bindOptionalParameter(
-                            "stringSlotA",
-                            properties -> {
-                                return Optional.ofNullable(
-                                        (Property<StringValue>) properties.get("stringSlotA")
-                                );
-                            },
-                            Arguments.Builder::setStringSlotA,
-                            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-                            TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
-                    .bindOptionalParameter(
-                            "stringSlotB",
-                            properties -> {
-                                return Optional.ofNullable(
-                                        (Property<StringValue>) properties.get("stringSlotB")
-                                );
-                            },
-                            Arguments.Builder::setStringSlotB,
-                            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-                            TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
-                    .build();
-
-    private CapabilityTwoStrings() {
-    }
-
-    /** Two required strings */
-    @AutoValue
-    public abstract static class Arguments {
-        public static Builder newBuilder() {
-            return new AutoValue_CapabilityTwoStrings_Arguments.Builder();
-        }
-
-        public abstract Optional<String> stringSlotA();
-
-        public abstract Optional<String> stringSlotB();
-
-        /** Builder for the testing Arguments. */
-        @AutoValue.Builder
-        public abstract static class Builder implements BuilderOf<Arguments> {
-
-            public abstract Builder setStringSlotA(@NonNull String value);
-
-            public abstract Builder setStringSlotB(@NonNull String value);
-
-            @NonNull
-            @Override
-            public abstract Arguments build();
-        }
-    }
-
-    /** Two required strings */
-    @AutoValue
-    public abstract static class Properties {
-        @NonNull
-        public static Builder newBuilder() {
-            return new AutoValue_CapabilityTwoStrings_Properties.Builder();
-        }
-
-        public abstract Optional<Property<StringValue>> stringSlotA();
-
-        public abstract Optional<Property<StringValue>> stringSlotB();
-
-        /** Builder for {@link Property} */
-        @AutoValue.Builder
-        public abstract static class Builder {
-
-            @NonNull
-            public abstract Builder setStringSlotA(@NonNull Property<StringValue> value);
-
-            @NonNull
-            public abstract Builder setStringSlotB(@NonNull Property<StringValue> value);
-
-            @NonNull
-            public abstract Properties build();
-        }
-    }
-
-    public interface ExecutionSession extends BaseExecutionSession<Arguments, Void> {}
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.kt
new file mode 100644
index 0000000..612d3ca
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+
+private const val CAPABILITY_NAME = "actions.intent.TEST"
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class CapabilityTwoStrings {
+
+    class Arguments internal constructor(
+        val stringSlotA: String?,
+        val stringSlotB: String?
+    ) {
+        override fun toString(): String {
+            return "Arguments(stringSlotA=$stringSlotA, " +
+                "stringSlotB=$stringSlotB)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Arguments
+
+            if (stringSlotA != other.stringSlotA) return false
+            if (stringSlotB != other.stringSlotB) return false
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = stringSlotA.hashCode()
+            result += 31 * stringSlotB.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Arguments> {
+            private var stringSlotA: String? = null
+            private var stringSlotB: String? = null
+
+            fun setStringSlotA(stringSlotA: String): Builder =
+                apply { this.stringSlotA = stringSlotA }
+
+            fun setStringSlotB(stringSlotB: String): Builder =
+                apply { this.stringSlotB = stringSlotB }
+
+            override fun build(): Arguments = Arguments(stringSlotA, stringSlotB)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+            .setArguments(Arguments::class.java, Arguments::Builder)
+            .setOutput(Output::class.java)
+            .bindParameter(
+                "stringSlotA",
+                { properties ->
+                    properties["stringSlotA"] as? Property<StringValue>
+                },
+                Arguments.Builder::setStringSlotA,
+                TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+            )
+            .bindParameter(
+                "stringSlotB",
+                { properties ->
+                    properties["stringSlotB"] as? Property<StringValue>
+                },
+                Arguments.Builder::setStringSlotB,
+                TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+            )
+            .build()
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Confirmation.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Confirmation.java
deleted file mode 100644
index 491a282..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Confirmation.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.Optional;
-
-/** Testing implementation of a capability Confirmation. */
-@AutoValue
-public abstract class Confirmation {
-
-    public static Builder builder() {
-        return new AutoValue_Confirmation.Builder();
-    }
-
-    public abstract Optional<String> optionalStringField();
-
-    @AutoValue.Builder
-    public abstract static class Builder {
-
-        public abstract Builder setOptionalStringField(String value);
-
-        public abstract Confirmation build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Confirmation.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Confirmation.kt
new file mode 100644
index 0000000..c66fce5
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Confirmation.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+class Confirmation internal constructor(
+    val optionalStringField: String?
+) {
+    override fun toString(): String {
+        return "Confirmation(optionalStringField=$optionalStringField)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as Confirmation
+
+        if (optionalStringField != other.optionalStringField) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return optionalStringField.hashCode()
+    }
+
+    class Builder {
+        private var optionalStringField: String? = null
+
+        fun setOptionalStringField(optionalStringField: String): Builder =
+            apply { this.optionalStringField = optionalStringField }
+
+        fun build(): Confirmation = Confirmation(optionalStringField)
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt
index 936f2b9..354a117 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/ExecutionSession.kt
@@ -23,7 +23,7 @@
 
 interface ExecutionSession : BaseExecutionSession<Arguments, Output> {
 
-    fun getRequiredStringListener(): AppEntityListener<String>? = null
+    val requiredStringListener: AppEntityListener<String>? get() = null
 
     companion object {
         @JvmStatic
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/GenericEntityArguments.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/GenericEntityArguments.kt
new file mode 100644
index 0000000..5c61176
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/GenericEntityArguments.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+
+class GenericEntityArguments internal constructor(
+    val singularField: TestEntity?,
+    val optionalField: TestEntity?,
+    val repeatedField: List<TestEntity>
+) {
+    override fun toString(): String {
+        return "GenericEntityArguments(singularField=$singularField, " +
+            "optionalField=$optionalField, " +
+            "repeatedField=$repeatedField, " +
+            ")"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as GenericEntityArguments
+
+        if (singularField != other.singularField) return false
+        if (optionalField != other.optionalField) return false
+        if (repeatedField != other.repeatedField) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = singularField.hashCode()
+        result += 31 * optionalField.hashCode()
+        result += 31 * repeatedField.hashCode()
+        return result
+    }
+
+    class Builder : BuilderOf<GenericEntityArguments> {
+        private var singularField: TestEntity? = null
+        private var optionalField: TestEntity? = null
+        private var repeatedField: List<TestEntity> = listOf()
+
+        fun setSingularField(singularField: TestEntity): Builder =
+            apply { this.singularField = singularField }
+
+        fun setOptionalField(optionalField: TestEntity): Builder =
+            apply { this.optionalField = optionalField }
+
+        fun setRepeatedField(repeatedField: List<TestEntity>): Builder =
+            apply { this.repeatedField = repeatedField }
+
+        override fun build(): GenericEntityArguments =
+            GenericEntityArguments(singularField, optionalField, repeatedField)
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Output.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Output.java
deleted file mode 100644
index 5464ff6..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Output.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-/** Testing implementation of a capability Output. */
-@AutoValue
-public abstract class Output {
-
-    public static Builder builder() {
-        return new AutoValue_Output.Builder().setRepeatedStringField(Collections.emptyList());
-    }
-
-    public abstract Optional<String> optionalStringField();
-
-    public abstract List<String> repeatedStringField();
-
-    @AutoValue.Builder
-    public abstract static class Builder {
-
-        public abstract Builder setOptionalStringField(String value);
-
-        public abstract Builder setRepeatedStringField(List<String> value);
-
-        public abstract Output build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Output.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Output.kt
new file mode 100644
index 0000000..1cc424f
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Output.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+class Output internal constructor(
+    val optionalStringField: String?,
+    val repeatedStringField: List<String>
+) {
+    override fun toString(): String {
+        return "Output(optionalStringField=$optionalStringField, " +
+            "repeatedStringField=$repeatedStringField)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as Output
+
+        if (optionalStringField != other.optionalStringField) return false
+        if (repeatedStringField != other.repeatedStringField) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = optionalStringField.hashCode()
+        result += 31 * repeatedStringField.hashCode()
+        return result
+    }
+
+    class Builder {
+        private var optionalStringField: String? = null
+        private var repeatedStringField: List<String> = listOf()
+
+        fun setOptionalStringField(optionalStringField: String): Builder =
+            apply { this.optionalStringField = optionalStringField }
+
+        fun setRepeatedStringField(repeatedStringField: List<String>): Builder = apply {
+            this.repeatedStringField = repeatedStringField
+        }
+
+        fun build(): Output = Output(optionalStringField, repeatedStringField)
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Properties.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Properties.java
deleted file mode 100644
index ee4846d..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Properties.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.properties.Property;
-import androidx.appactions.interaction.capabilities.core.properties.StringValue;
-
-import com.google.auto.value.AutoValue;
-
-import java.util.Optional;
-
-/** Testing implementation of a capability Property. */
-@AutoValue
-public abstract class Properties {
-
-    public static Builder newBuilder() {
-        return new AutoValue_Properties.Builder();
-    }
-
-    public abstract Property<StringValue> requiredStringField();
-
-    public abstract Optional<Property<StringValue>> optionalStringField();
-
-    public abstract Optional<Property<TestEnum>> enumField();
-
-    public abstract Optional<Property<StringValue>> repeatedStringField();
-
-    /** Builder for the testing Property. */
-    @AutoValue.Builder
-    public abstract static class Builder implements BuilderOf<Properties> {
-
-        public abstract Builder setRequiredStringField(Property<StringValue> property);
-
-        public abstract Builder setOptionalStringField(Property<StringValue> property);
-
-        public abstract Builder setEnumField(Property<TestEnum> property);
-
-        public abstract Builder setRepeatedStringField(Property<StringValue> property);
-
-        @NonNull
-        @Override
-        public abstract Properties build();
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java
deleted file mode 100644
index a8d103c..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing.spec;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-
-import com.google.auto.value.AutoValue;
-
-import java.time.Duration;
-import java.time.ZonedDateTime;
-import java.util.Optional;
-
-/** A test class for capability value. */
-@AutoValue
-public abstract class TestEntity {
-
-    public static Builder newBuilder() {
-        return new AutoValue_TestEntity.Builder();
-    }
-
-    public abstract Optional<String> getId();
-
-    public abstract Optional<String> getName();
-
-    public abstract Optional<Duration> getDuration();
-
-    public abstract Optional<ZonedDateTime> getZonedDateTime();
-
-    public abstract Optional<TestEnum> getEnum();
-
-    public abstract Optional<TestEntity> getEntity();
-
-    public enum TestEnum {
-        VALUE_1("value_1"),
-        VALUE_2("value_2");
-
-        private final String mStringValue;
-
-        TestEnum(String stringValue) {
-            this.mStringValue = stringValue;
-        }
-
-        @NonNull
-        @Override
-        public String toString() {
-            return mStringValue;
-        }
-    }
-
-    @AutoValue.Builder
-    public abstract static class Builder implements BuilderOf<TestEntity> {
-
-        public abstract Builder setId(String id);
-
-        public abstract Builder setName(String name);
-
-        public abstract Builder setDuration(Duration duration);
-
-        public abstract Builder setZonedDateTime(ZonedDateTime date);
-
-        public abstract Builder setEnum(TestEnum enumValue);
-
-        public abstract Builder setEntity(TestEntity entity);
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.kt
new file mode 100644
index 0000000..aa478c4
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing.spec
+
+import java.time.Duration
+import java.time.ZonedDateTime
+
+class TestEntity internal constructor(
+    val id: String?,
+    val name: String?,
+    val duration: Duration?,
+    val zonedDateTime: ZonedDateTime?,
+    val enum: TestEnum?,
+    val entity: TestEntity?
+) {
+    override fun toString(): String {
+        return "TestEntity(identifier=$id, name=$name, duration=$duration, " +
+            "zonedDateTime=$zonedDateTime, enum=$enum, entity=$entity)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as TestEntity
+
+        if (id != other.id) return false
+        if (name != other.name) return false
+        if (duration != other.duration) return false
+        if (zonedDateTime != other.zonedDateTime) return false
+        if (enum != other.enum) return false
+        if (entity != other.entity) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = id.hashCode()
+        result += 31 * name.hashCode()
+        result += 31 * duration.hashCode()
+        result += 31 * zonedDateTime.hashCode()
+        result += 31 * enum.hashCode()
+        result += 31 * entity.hashCode()
+        return result
+    }
+
+    class Builder {
+        private var id: String? = null
+        private var name: String? = null
+        private var duration: Duration? = null
+        private var zonedDateTime: ZonedDateTime? = null
+        private var enum: TestEnum? = null
+        private var entity: TestEntity? = null
+
+        fun setId(id: String): Builder = apply { this.id = id }
+        fun setName(name: String): Builder = apply { this.name = name }
+        fun setDuration(duration: Duration): Builder = apply { this.duration = duration }
+        fun setZonedDateTime(zonedDateTime: ZonedDateTime): Builder = apply {
+            this.zonedDateTime = zonedDateTime
+        }
+        fun setEnum(enum: TestEnum): Builder = apply { this.enum = enum }
+        fun setEntity(entity: TestEntity): Builder = apply { this.entity = entity }
+
+        fun build(): TestEntity = TestEntity(id, name, duration, zonedDateTime, enum, entity)
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.kt
similarity index 78%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.kt
index c325c57..7c81862 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.kt
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.appactions.interaction.capabilities.core.testing.spec;
+package androidx.appactions.interaction.capabilities.core.testing.spec
 
-/** Sample enum value for testing. */
-public enum TestEnum {
-    VALUE_1,
-    VALUE_2,
-}
+enum class TestEnum(private val mStringValue: String) {
+    VALUE_1("VALUE_1"),
+    VALUE_2("VALUE_2");
+
+    override fun toString(): String {
+        return mStringValue
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
index d530c21..4fd5dc7 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
@@ -24,51 +24,15 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.time.LocalTime
-import java.util.Optional
 
-/** GetExerciseObservation.kt in interaction-capabilities-fitness */
-private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
+private const val CAPABILITY_NAME = "actions.intent.GET_EXERCISE_OBSERVATION"
 
-// TODO(b/273602015): Update to use Name property from builtintype library.
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(
-            GetExerciseObservation.Arguments::class.java,
-            GetExerciseObservation.Arguments::Builder
-        )
-        .setOutput(GetExerciseObservation.Output::class.java)
-        .bindOptionalParameter(
-            "exerciseObservation.startTime",
-            { properties ->
-                Optional.ofNullable(
-                    properties[GetExerciseObservation.PropertyMapStrings.START_TIME.key]
-                        as Property<LocalTime>
-                )
-            },
-            GetExerciseObservation.Arguments.Builder::setStartTime,
-            TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
-            TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
-        )
-        .bindOptionalParameter(
-            "exerciseObservation.endTime",
-            { properties ->
-                Optional.ofNullable(
-                    properties[GetExerciseObservation.PropertyMapStrings.END_TIME.key]
-                        as Property<LocalTime>
-                )
-            },
-            GetExerciseObservation.Arguments.Builder::setEndTime,
-            TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
-            TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.GET_EXERCISE_OBSERVATION */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class GetExerciseObservation private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
         START_TIME("exerciseObservation.startTime"),
-        END_TIME("exerciseObservation.endTime"),
+        END_TIME("exerciseObservation.endTime")
     }
 
     class CapabilityBuilder :
@@ -134,4 +98,35 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        // TODO(b/273602015): Update to use Name property from builtintype library.
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(
+                    Arguments::class.java,
+                    Arguments::Builder
+                )
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "exerciseObservation.startTime",
+                    { properties ->
+                        properties[PropertyMapStrings.START_TIME.key] as? Property<LocalTime>
+                    },
+                    Arguments.Builder::setStartTime,
+                    TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
+                    TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
+                )
+                .bindParameter(
+                    "exerciseObservation.endTime",
+                    { properties ->
+                        properties[PropertyMapStrings.END_TIME.key] as? Property<LocalTime>
+                    },
+                    Arguments.Builder::setEndTime,
+                    TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
+                    TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
index c7482e1..7d843ac 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
@@ -24,51 +24,16 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import java.time.LocalTime
-import java.util.Optional
 
-/** GetHealthObservation.kt in interaction-capabilities-fitness */
-private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
+private const val CAPABILITY_NAME = "actions.intent.GET_HEALTH_OBSERVATION"
 
-// TODO(b/273602015): Update to use Name property from builtintype library.
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(
-            GetHealthObservation.Arguments::class.java,
-            GetHealthObservation.Arguments::Builder
-        )
-        .setOutput(GetHealthObservation.Output::class.java)
-        .bindOptionalParameter(
-            "healthObservation.startTime",
-            { properties ->
-                Optional.ofNullable(
-                    properties[GetHealthObservation.PropertyMapStrings.START_TIME.key]
-                        as Property<LocalTime>
-                )
-            },
-            GetHealthObservation.Arguments.Builder::setStartTime,
-            TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
-            TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
-        )
-        .bindOptionalParameter(
-            "healthObservation.endTime",
-            { properties ->
-                Optional.ofNullable(
-                    properties[GetHealthObservation.PropertyMapStrings.END_TIME.key]
-                        as Property<LocalTime>
-                )
-            },
-            GetHealthObservation.Arguments.Builder::setEndTime,
-            TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
-            TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.GET_HEALTH_OBSERVATION */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class GetHealthObservation private constructor() {
+
     internal enum class PropertyMapStrings(val key: String) {
         START_TIME("healthObservation.startTime"),
-        END_TIME("healthObservation.endTime"),
+        END_TIME("healthObservation.endTime")
     }
 
     class CapabilityBuilder :
@@ -138,4 +103,35 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        // TODO(b/273602015): Update to use Name property from builtintype library.
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(
+                    Arguments::class.java,
+                    Arguments::Builder
+                )
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "healthObservation.startTime",
+                    { properties ->
+                        properties[PropertyMapStrings.START_TIME.key] as? Property<LocalTime>
+                    },
+                    Arguments.Builder::setStartTime,
+                    TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
+                    TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
+                )
+                .bindParameter(
+                    "healthObservation.endTime",
+                    { properties ->
+                        properties[PropertyMapStrings.END_TIME.key] as? Property<LocalTime>
+                    },
+                    Arguments.Builder::setEndTime,
+                    TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
+                    TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
index f04ceee..8a94643d 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
@@ -24,35 +24,14 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import java.util.Optional
 
-/** PauseExercise.kt in interaction-capabilities-fitness */
 private const val CAPABILITY_NAME = "actions.intent.PAUSE_EXERCISE"
 
-// TODO(b/273602015): Update to use Name property from builtintype library.
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(PauseExercise.Arguments::class.java, PauseExercise.Arguments::Builder)
-        .setOutput(PauseExercise.Output::class.java)
-        .bindOptionalParameter(
-            "exercise.name",
-            { properties ->
-                Optional.ofNullable(
-                    properties[PauseExercise.PropertyMapStrings.NAME.key]
-                        as Property<StringValue>
-                )
-            },
-            PauseExercise.Arguments.Builder::setName,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.PAUSE_EXERCISE */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class PauseExercise private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        NAME("exercise.name"),
+        NAME("exercise.name")
     }
 
     class CapabilityBuilder :
@@ -110,4 +89,23 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        // TODO(b/273602015): Update to use Name property from builtintype library.
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "exercise.name",
+                    { properties ->
+                        properties[PropertyMapStrings.NAME.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setName,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
index 1881a7c..963f7ad 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
@@ -24,35 +24,14 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import java.util.Optional
 
-/** ResumeExercise.kt in interaction-capabilities-fitness */
 private const val CAPABILITY_NAME = "actions.intent.RESUME_EXERCISE"
 
-// TODO(b/273602015): Update to use Name property from builtintype library.
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(ResumeExercise.Arguments::class.java, ResumeExercise.Arguments::Builder)
-        .setOutput(ResumeExercise.Output::class.java)
-        .bindOptionalParameter(
-            "exercise.name",
-            { properties ->
-                Optional.ofNullable(
-                    properties[ResumeExercise.PropertyMapStrings.NAME.key]
-                        as Property<StringValue>
-                )
-            },
-            ResumeExercise.Arguments.Builder::setName,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.RESUME_EXERCISE */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class ResumeExercise private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        NAME("exercise.name"),
+        NAME("exercise.name")
     }
 
     class CapabilityBuilder :
@@ -110,4 +89,23 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        // TODO(b/273602015): Update to use Name property from builtintype library.
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "exercise.name",
+                    { properties ->
+                        properties[PropertyMapStrings.NAME.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setName,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
index 0847d33..3de87ec 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
@@ -25,48 +25,15 @@
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
 import java.time.Duration
-import java.util.Optional
 
-/** StartExercise.kt in interaction-capabilities-fitness */
 private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
 
-// TODO(b/273602015): Update to use Name property from builtintype library.
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(StartExercise.Arguments::class.java, StartExercise.Arguments::Builder)
-        .setOutput(StartExercise.Output::class.java)
-        .bindOptionalParameter(
-            "exercise.duration",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StartExercise.PropertyMapStrings.DURATION.key]
-                        as Property<Duration>
-                )
-            },
-            StartExercise.Arguments.Builder::setDuration,
-            TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
-            TypeConverters.DURATION_ENTITY_CONVERTER
-        )
-        .bindOptionalParameter(
-            "exercise.name",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StartExercise.PropertyMapStrings.NAME.key]
-                        as Property<StringValue>
-                )
-            },
-            StartExercise.Arguments.Builder::setName,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.START_EXERCISE */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class StartExercise private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
         NAME("exercise.name"),
-        DURATION("exercise.duration"),
+        DURATION("exercise.duration")
     }
 
     class CapabilityBuilder :
@@ -136,4 +103,32 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        // TODO(b/273602015): Update to use Name property from builtintype library.
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "exercise.duration",
+                    { properties ->
+                        properties[PropertyMapStrings.DURATION.key] as? Property<Duration>
+                    },
+                    Arguments.Builder::setDuration,
+                    TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
+                    TypeConverters.DURATION_ENTITY_CONVERTER
+                )
+                .bindParameter(
+                    "exercise.name",
+                    { properties ->
+                        properties[PropertyMapStrings.NAME.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setName,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
index 18efde0..e257fac 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
@@ -24,35 +24,14 @@
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import java.util.Optional
 
-/** StopExercise.kt in interaction-capabilities-fitness */
-private const val CAPABILITY_NAME = "actions.intent.PAUSE_EXERCISE"
+private const val CAPABILITY_NAME = "actions.intent.STOP_EXERCISE"
 
-// TODO(b/273602015): Update to use Name property from builtintype library.
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(StopExercise.Arguments::class.java, StopExercise.Arguments::Builder)
-        .setOutput(StopExercise.Output::class.java)
-        .bindOptionalParameter(
-            "exercise.name",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StopExercise.PropertyMapStrings.NAME.key]
-                        as Property<StringValue>
-                )
-            },
-            StopExercise.Arguments.Builder::setName,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER
-        )
-        .build()
-
+/** A capability corresponding to actions.intent.STOP_EXERCISE */
 @CapabilityFactory(name = CAPABILITY_NAME)
 class StopExercise private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        NAME("exercise.name"),
+        NAME("exercise.name")
     }
 
     class CapabilityBuilder :
@@ -111,4 +90,23 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        // TODO(b/273602015): Update to use Name property from builtintype library.
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "exercise.name",
+                    { properties ->
+                        properties[PropertyMapStrings.NAME.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setName,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
index 528a497..260c074 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
@@ -20,6 +20,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -27,39 +28,14 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** PauseTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.PAUSE_TIMER"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(PauseTimer.Arguments::class.java, PauseTimer.Arguments::Builder)
-        .setOutput(PauseTimer.Output::class.java)
-        .bindRepeatedParameter(
-            "timer",
-            { properties ->
-                Optional.ofNullable(
-                    properties[PauseTimer.PropertyMapStrings.TIMER_LIST.key]
-                        as Property<TimerValue>
-                )
-            },
-            PauseTimer.Arguments.Builder::setTimerList,
-            TimerValue.PARAM_VALUE_CONVERTER,
-            TimerValue.ENTITY_CONVERTER
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            PauseTimer.ExecutionStatus::toParamValue,
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.PAUSE_TIMER */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class PauseTimer private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        TIMER_LIST("timer.timerList"),
+        TIMER_LIST("timer.timerList")
     }
 
     class CapabilityBuilder :
@@ -83,7 +59,7 @@
 
     class Arguments
     internal constructor(
-        val timerList: List<TimerValue>?,
+        val timerList: List<TimerValue>?
     ) {
         override fun toString(): String {
             return "Arguments(timerList=$timerList)"
@@ -169,7 +145,7 @@
             val value: Value = Value.newBuilder().setStringValue(status).build()
             return ParamValue.newBuilder()
                 .setStructValue(
-                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
                 )
                 .build()
         }
@@ -178,4 +154,27 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindRepeatedParameter(
+                    "timer",
+                    { properties ->
+                        properties[PropertyMapStrings.TIMER_LIST.key] as? Property<TimerValue>
+                    },
+                    Arguments.Builder::setTimerList,
+                    TimerValue.PARAM_VALUE_CONVERTER,
+                    TimerValue.ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
index 94d203b..8fb648c 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
@@ -20,6 +20,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -27,39 +28,14 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** ResetTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.RESET_TIMER"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(ResetTimer.Arguments::class.java, ResetTimer.Arguments::Builder)
-        .setOutput(ResetTimer.Output::class.java)
-        .bindRepeatedParameter(
-            "timer",
-            { properties ->
-                Optional.ofNullable(
-                    properties[ResetTimer.PropertyMapStrings.TIMER_LIST.key]
-                        as Property<TimerValue>
-                )
-            },
-            ResetTimer.Arguments.Builder::setTimerList,
-            TimerValue.PARAM_VALUE_CONVERTER,
-            TimerValue.ENTITY_CONVERTER
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            ResetTimer.ExecutionStatus::toParamValue
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.RESET_TIMER */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class ResetTimer private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        TIMER_LIST("timer.timerList"),
+        TIMER_LIST("timer.timerList")
     }
 
     class CapabilityBuilder :
@@ -105,7 +81,7 @@
             private var timerList: List<TimerValue>? = null
 
             fun setTimerList(
-                timerList: List<TimerValue>,
+                timerList: List<TimerValue>
             ): Builder = apply { this.timerList = timerList }
 
             override fun build(): Arguments = Arguments(timerList)
@@ -166,7 +142,7 @@
             val value: Value = Value.newBuilder().setStringValue(status).build()
             return ParamValue.newBuilder()
                 .setStructValue(
-                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
                 )
                 .build()
         }
@@ -175,4 +151,27 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindRepeatedParameter(
+                    "timer",
+                    { properties ->
+                        properties[PropertyMapStrings.TIMER_LIST.key] as? Property<TimerValue>
+                    },
+                    Arguments.Builder::setTimerList,
+                    TimerValue.PARAM_VALUE_CONVERTER,
+                    TimerValue.ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
index d967e45..702d431 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
@@ -20,6 +20,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -27,39 +28,14 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** ResumeTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.RESUME_TIMER"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(ResumeTimer.Arguments::class.java, ResumeTimer.Arguments::Builder)
-        .setOutput(ResumeTimer.Output::class.java)
-        .bindRepeatedParameter(
-            "timer",
-            { properties ->
-                Optional.ofNullable(
-                    properties[ResumeTimer.PropertyMapStrings.TIMER_LIST.key]
-                        as Property<TimerValue>
-                )
-            },
-            ResumeTimer.Arguments.Builder::setTimerList,
-            TimerValue.PARAM_VALUE_CONVERTER,
-            TimerValue.ENTITY_CONVERTER
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            ResumeTimer.ExecutionStatus::toParamValue
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.RESUME_TIMER */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class ResumeTimer private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        TIMER_LIST("timer.timerList"),
+        TIMER_LIST("timer.timerList")
     }
 
     class CapabilityBuilder :
@@ -105,7 +81,7 @@
             private var timerList: List<TimerValue>? = null
 
             fun setTimerList(
-                timerList: List<TimerValue>,
+                timerList: List<TimerValue>
             ): Builder = apply { this.timerList = timerList }
 
             override fun build(): Arguments = Arguments(timerList)
@@ -166,7 +142,7 @@
             val value: Value = Value.newBuilder().setStringValue(status).build()
             return ParamValue.newBuilder()
                 .setStructValue(
-                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
                 )
                 .build()
         }
@@ -175,4 +151,27 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindRepeatedParameter(
+                    "timer",
+                    { properties ->
+                        properties[PropertyMapStrings.TIMER_LIST.key] as? Property<TimerValue>
+                    },
+                    Arguments.Builder::setTimerList,
+                    TimerValue.PARAM_VALUE_CONVERTER,
+                    TimerValue.ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
index fcd211c..6ffb63a 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
@@ -20,6 +20,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.ValueListener
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
@@ -33,86 +34,17 @@
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
 import java.time.Duration
-import java.util.Optional
 
-/** StartTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.START_TIMER"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(StartTimer.Arguments::class.java, StartTimer.Arguments::Builder)
-        .setOutput(StartTimer.Output::class.java)
-        .bindOptionalParameter(
-            "timer.identifier",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StartTimer.PropertyMapStrings.IDENTIFIER.key]
-                        as Property<StringValue>
-                )
-            },
-            StartTimer.Arguments.Builder::setIdentifier,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
-        )
-        .bindOptionalParameter(
-            "timer.name",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StartTimer.PropertyMapStrings.NAME.key]
-                        as Property<StringValue>
-                )
-            },
-            StartTimer.Arguments.Builder::setName,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-            TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
-        )
-        .bindOptionalParameter(
-            "timer.duration",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StartTimer.PropertyMapStrings.DURATION.key]
-                        as Property<Duration>
-                )
-            },
-            StartTimer.Arguments.Builder::setDuration,
-            TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
-            TypeConverters.DURATION_ENTITY_CONVERTER,
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            StartTimer.ExecutionStatus::toParamValue,
-        )
-        .build()
-
-private val SESSION_BRIDGE = SessionBridge<StartTimer.ExecutionSession, StartTimer.Confirmation> {
-        session ->
-    val taskHandlerBuilder = TaskHandler.Builder<StartTimer.Confirmation>()
-    session.nameListener?.let {
-        taskHandlerBuilder.registerValueTaskParam(
-            "timer.name",
-            it,
-            TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-        )
-    }
-    session.durationListener?.let {
-        taskHandlerBuilder.registerValueTaskParam(
-            "timer.duration",
-            it,
-            TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
-        )
-    }
-    taskHandlerBuilder.build()
-}
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.START_TIMER */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class StartTimer private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
         TIMER_LIST("timer.timerList"),
         IDENTIFIER("timer.identifier"),
         NAME("timer.name"),
-        DURATION("timer.duration"),
+        DURATION("timer.duration")
     }
 
     class CapabilityBuilder :
@@ -125,10 +57,14 @@
             >(ACTION_SPEC) {
         private var properties = mutableMapOf<String, Property<*>>()
 
-        override val sessionBridge: SessionBridge<ExecutionSession, Confirmation> = SESSION_BRIDGE
+        override val sessionBridge: SessionBridge<
+            ExecutionSession,
+            Arguments,
+            Confirmation
+        > = SESSION_BRIDGE
 
         override fun setExecutionSessionFactory(
-            sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession,
+            sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession
         ): CapabilityBuilder = super.setExecutionSessionFactory(sessionFactory)
 
         fun setTimerList(timerList: Property<TimerValue>): CapabilityBuilder = apply {
@@ -163,7 +99,7 @@
     class Arguments internal constructor(
         val identifier: String?,
         val name: String?,
-        val duration: Duration?,
+        val duration: Duration?
     ) {
         override fun toString(): String {
             return "Arguments(identifier=$identifier,name=$name,duration=$duration)"
@@ -258,11 +194,72 @@
             val value: Value = Value.newBuilder().setStringValue(status).build()
             return ParamValue.newBuilder()
                 .setStructValue(
-                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
                 )
                 .build()
         }
     }
 
     class Confirmation internal constructor()
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "timer.identifier",
+                    { properties ->
+                        properties[PropertyMapStrings.IDENTIFIER.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setIdentifier,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .bindParameter(
+                    "timer.name",
+                    { properties ->
+                        properties[PropertyMapStrings.NAME.key] as? Property<StringValue>
+                    },
+                    Arguments.Builder::setName,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                    TypeConverters.STRING_VALUE_ENTITY_CONVERTER
+                )
+                .bindParameter(
+                    "timer.duration",
+                    { properties ->
+                        properties[PropertyMapStrings.DURATION.key] as? Property<Duration>
+                    },
+                    Arguments.Builder::setDuration,
+                    TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
+                    TypeConverters.DURATION_ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+
+        private val SESSION_BRIDGE = SessionBridge<ExecutionSession, Arguments, Confirmation> {
+                session ->
+            val taskHandlerBuilder = TaskHandler.Builder<Arguments, Confirmation>()
+            session.nameListener?.let {
+                taskHandlerBuilder.registerValueTaskParam(
+                    "timer.name",
+                    it,
+                    TypeConverters.STRING_PARAM_VALUE_CONVERTER
+                )
+            }
+            session.durationListener?.let {
+                taskHandlerBuilder.registerValueTaskParam(
+                    "timer.duration",
+                    it,
+                    TypeConverters.DURATION_PARAM_VALUE_CONVERTER
+                )
+            }
+            taskHandlerBuilder.build()
+        }
     }
+}
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
index 93f5b75..4c279e2 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
@@ -20,6 +20,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -27,39 +28,14 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** StopTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.STOP_TIMER"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(StopTimer.Arguments::class.java, StopTimer.Arguments::Builder)
-        .setOutput(StopTimer.Output::class.java)
-        .bindRepeatedParameter(
-            "timer",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StopTimer.PropertyMapStrings.TIMER_LIST.key]
-                        as Property<TimerValue>
-                )
-            },
-            StopTimer.Arguments.Builder::setTimerList,
-            TimerValue.PARAM_VALUE_CONVERTER,
-            TimerValue.ENTITY_CONVERTER
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            StopTimer.ExecutionStatus::toParamValue
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.STOP_TIMER */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class StopTimer private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
-        TIMER_LIST("timer.timerList"),
+        TIMER_LIST("timer.timerList")
     }
 
     class CapabilityBuilder :
@@ -105,7 +81,7 @@
             private var timerList: List<TimerValue>? = null
 
             fun setTimerList(
-                timerList: List<TimerValue>,
+                timerList: List<TimerValue>
             ): Builder = apply { this.timerList = timerList }
 
             override fun build(): Arguments = Arguments(timerList)
@@ -166,7 +142,7 @@
             val value: Value = Value.newBuilder().setStringValue(status).build()
             return ParamValue.newBuilder()
                 .setStructValue(
-                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
                 )
                 .build()
         }
@@ -175,4 +151,27 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindRepeatedParameter(
+                    "timer",
+                    { properties ->
+                        properties[PropertyMapStrings.TIMER_LIST.key] as? Property<TimerValue>
+                    },
+                    Arguments.Builder::setTimerList,
+                    TimerValue.PARAM_VALUE_CONVERTER,
+                    TimerValue.ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
index 11e0682..908051a 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
@@ -21,6 +21,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -31,26 +32,11 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** StartEmergencySharing.kt in interaction-capabilities-safety */
 private const val CAPABILITY_NAME = "actions.intent.START_EMERGENCY_SHARING"
 
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(
-            StartEmergencySharing.Arguments::class.java,
-            StartEmergencySharing.Arguments::Builder
-        )
-        .setOutput(StartEmergencySharing.Output::class.java)
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            StartEmergencySharing.ExecutionStatus::toParamValue,
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.START_EMERGENCY_SHARING */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class StartEmergencySharing private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
@@ -167,4 +153,20 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(
+                    Arguments::class.java,
+                    Arguments::Builder
+                )
+                .setOutput(Output::class.java)
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue,
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
index 9bb6f01..5a9a6e2 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
@@ -23,6 +23,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
@@ -37,57 +38,15 @@
 import androidx.appactions.interaction.protobuf.Value
 import java.time.Duration
 import java.time.ZonedDateTime
-import java.util.Optional
 
-/** StartSafetyCheck.kt in interaction-capabilities-safety */
 private const val CAPABILITY_NAME = "actions.intent.START_SAFETY_CHECK"
 
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(StartSafetyCheck.Arguments::class.java, StartSafetyCheck.Arguments::Builder)
-        .setOutput(StartSafetyCheck.Output::class.java)
-        .bindOptionalParameter(
-            "safetyCheck.duration",
-            { properties ->
-                Optional.ofNullable(
-                    properties[StartSafetyCheck.PropertyMapStrings.DURATION.key]
-                        as Property<Duration>
-                )
-            },
-            StartSafetyCheck.Arguments.Builder::setDuration,
-            TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
-            TypeConverters.DURATION_ENTITY_CONVERTER
-        )
-        .bindOptionalParameter(
-            "safetyCheck.checkInTime",
-            { property ->
-                Optional.ofNullable(
-                    property[StartSafetyCheck.PropertyMapStrings.CHECK_IN_TIME.key]
-                        as Property<ZonedDateTime>
-                )
-            },
-            StartSafetyCheck.Arguments.Builder::setCheckInTime,
-            TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER,
-            TypeConverters.ZONED_DATETIME_ENTITY_CONVERTER
-        )
-        .bindOptionalOutput(
-            "safetyCheck",
-            { output -> Optional.ofNullable(output.safetyCheck) },
-            ParamValueConverter.of(SAFETY_CHECK_TYPE_SPEC)::toParamValue
-        )
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            StartSafetyCheck.ExecutionStatus::toParamValue
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.START_SAFETY_CHECK */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class StartSafetyCheck private constructor() {
     internal enum class PropertyMapStrings(val key: String) {
         DURATION("safetycheck.duration"),
-        CHECK_IN_TIME("safetycheck.checkInTime"),
+        CHECK_IN_TIME("safetycheck.checkInTime")
     }
 
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
@@ -256,7 +215,7 @@
                 .setStructValue(
                     Struct.newBuilder()
                         .putFields(TypeConverters.FIELD_NAME_TYPE, value)
-                        .build(),
+                        .build()
                 )
                 .build()
         }
@@ -265,4 +224,41 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindParameter(
+                    "safetyCheck.duration",
+                    { properties ->
+                        properties[PropertyMapStrings.DURATION.key] as? Property<Duration>
+                    },
+                    Arguments.Builder::setDuration,
+                    TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
+                    TypeConverters.DURATION_ENTITY_CONVERTER
+                )
+                .bindParameter(
+                    "safetyCheck.checkInTime",
+                    { properties ->
+                        properties[PropertyMapStrings.CHECK_IN_TIME.key] as? Property<ZonedDateTime>
+                    },
+                    Arguments.Builder::setCheckInTime,
+                    TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER,
+                    TypeConverters.ZONED_DATETIME_ENTITY_CONVERTER
+                )
+                .bindOutput(
+                    "safetyCheck",
+                    Output::safetyCheck,
+                    ParamValueConverter.of(SAFETY_CHECK_TYPE_SPEC)::toParamValue
+                )
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
index 731aff5..28967d6 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
@@ -22,6 +22,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -31,26 +32,11 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** StopEmergencySharing.kt in interaction-capabilities-safety */
 private const val CAPABILITY_NAME = "actions.intent.STOP_EMERGENCY_SHARING"
 
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(
-            StopEmergencySharing.Arguments::class.java,
-            StopEmergencySharing.Arguments::Builder
-        )
-        .setOutput(StopEmergencySharing.Output::class.java)
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            StopEmergencySharing.ExecutionStatus::toParamValue,
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.STOP_EMERGENCY_SHARING */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class StopEmergencySharing private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
@@ -168,4 +154,20 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(
+                    Arguments::class.java,
+                    Arguments::Builder
+                )
+                .setOutput(Output::class.java)
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue,
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
index 3d3ffed..53893f4 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
@@ -22,6 +22,7 @@
 import androidx.appactions.builtintypes.experimental.types.SuccessStatus
 import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
 import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -31,23 +32,11 @@
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
-import java.util.Optional
 
-/** StopSafetyCheck.kt in interaction-capabilities-safety */
 private const val CAPABILITY_NAME = "actions.intent.STOP_SAFETY_CHECK"
 
-private val ACTION_SPEC =
-    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-        .setArguments(StopSafetyCheck.Arguments::class.java, StopSafetyCheck.Arguments::Builder)
-        .setOutput(StopSafetyCheck.Output::class.java)
-        .bindOptionalOutput(
-            "executionStatus",
-            { output -> Optional.ofNullable(output.executionStatus) },
-            StopSafetyCheck.ExecutionStatus::toParamValue
-        )
-        .build()
-
-// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
+/** A capability corresponding to actions.intent.STOP_SAFETY_CHECK */
+@CapabilityFactory(name = CAPABILITY_NAME)
 class StopSafetyCheck private constructor() {
     // TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
     class CapabilityBuilder :
@@ -164,4 +153,17 @@
     class Confirmation internal constructor()
 
     sealed interface ExecutionSession : BaseExecutionSession<Arguments, Output>
+
+    companion object {
+        private val ACTION_SPEC =
+            ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+                .setArguments(Arguments::class.java, Arguments::Builder)
+                .setOutput(Output::class.java)
+                .bindOutput(
+                    "executionStatus",
+                    Output::executionStatus,
+                    ExecutionStatus::toParamValue
+                )
+                .build()
+    }
 }
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
index 9708c4a..1105d83 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
@@ -26,19 +26,8 @@
 import androidx.appactions.interaction.capabilities.core.impl.task.TaskHandler
 import androidx.appactions.interaction.capabilities.core.properties.Property
 import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import java.util.Optional
 
 private const val CAPABILITY_NAME = "actions.intent.FAKE_CAPABILITY"
-@Suppress("UNCHECKED_CAST")
-private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setArguments(FakeCapability.Arguments::class.java, FakeCapability.Arguments::Builder)
-    .setOutput(FakeCapability.Output::class.java).bindOptionalParameter(
-        "fieldOne",
-        { property -> Optional.ofNullable(property["fieldOne"] as Property<StringValue>) },
-        FakeCapability.Arguments.Builder::setFieldOne,
-        TypeConverters.STRING_PARAM_VALUE_CONVERTER,
-        TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
-    ).build()
 
 class FakeCapability private constructor() {
     class Properties(
@@ -73,9 +62,9 @@
         Confirmation,
         ExecutionSession,
         >(ACTION_SPEC) {
-        override val sessionBridge = SessionBridge<ExecutionSession, Confirmation> {
+        override val sessionBridge = SessionBridge<ExecutionSession, Arguments, Confirmation> {
                 session ->
-            val builder = TaskHandler.Builder<Confirmation>()
+            val builder = TaskHandler.Builder<Arguments, Confirmation>()
             session.fieldOneListener?.let {
                 builder.registerValueTaskParam(
                     "fieldOne",
@@ -103,4 +92,19 @@
             return super.build()
         }
     }
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+            .setArguments(Arguments::class.java, Arguments::Builder)
+            .setOutput(Output::class.java)
+            .bindParameter(
+                "fieldOne",
+                { properties -> properties["fieldOne"] as? Property<StringValue> },
+                Arguments.Builder::setFieldOne,
+                TypeConverters.STRING_PARAM_VALUE_CONVERTER,
+                TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
+            )
+            .build()
+    }
 }
diff --git a/appcompat/appcompat-resources/src/main/AndroidManifest.xml b/appcompat/appcompat-resources/src/main/AndroidManifest.xml
deleted file mode 100644
index 2766785..0000000
--- a/appcompat/appcompat-resources/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTest.kt
index 38d9ab2..5d3fbee 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTest.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTest.kt
@@ -30,6 +30,7 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.TimeUnit
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,6 +54,7 @@
     }
 
     @Test
+    @Ignore("b/280630647")
     fun testConfigurationUpdatedOnLandscapeMode() {
         // Wait for the activity to fully start before rotating,
         // otherwise we won't receive onConfigurationChanged.
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt
index 45357c5..eba4c96 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt
@@ -32,6 +32,7 @@
 import java.util.concurrent.TimeUnit
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,6 +71,7 @@
     }
 
     @Test
+    @Ignore("b/280630647")
     fun testConfigurationUpdatedOnLandscapeMode() {
         // Wait for the activity to fully start before rotating,
         // otherwise we won't receive onConfigurationChanged.
diff --git a/appcompat/appcompat/src/main/AndroidManifest.xml b/appcompat/appcompat/src/main/AndroidManifest.xml
deleted file mode 100644
index 2766785..0000000
--- a/appcompat/appcompat/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/appsearch/appsearch-builtin-types/src/main/AndroidManifest.xml b/appsearch/appsearch-builtin-types/src/main/AndroidManifest.xml
deleted file mode 100644
index a270226..0000000
--- a/appsearch/appsearch-builtin-types/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
\ No newline at end of file
diff --git a/appsearch/appsearch-debug-view/src/main/AndroidManifest.xml b/appsearch/appsearch-debug-view/src/main/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/appsearch/appsearch-debug-view/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/appsearch/appsearch-ktx/src/main/AndroidManifest.xml b/appsearch/appsearch-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index adbf0a8..0000000
--- a/appsearch/appsearch-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
diff --git a/appsearch/appsearch-local-storage/src/main/AndroidManifest.xml b/appsearch/appsearch-local-storage/src/main/AndroidManifest.xml
deleted file mode 100644
index e95a361..0000000
--- a/appsearch/appsearch-local-storage/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
diff --git a/appsearch/appsearch-platform-storage/src/main/AndroidManifest.xml b/appsearch/appsearch-platform-storage/src/main/AndroidManifest.xml
deleted file mode 100644
index adbf0a8..0000000
--- a/appsearch/appsearch-platform-storage/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
diff --git a/appsearch/appsearch-test-util/src/main/AndroidManifest.xml b/appsearch/appsearch-test-util/src/main/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/appsearch/appsearch-test-util/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/appsearch/appsearch/src/main/AndroidManifest.xml b/appsearch/appsearch/src/main/AndroidManifest.xml
deleted file mode 100644
index 19e768b..0000000
--- a/appsearch/appsearch/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
\ No newline at end of file
diff --git a/asynclayoutinflater/asynclayoutinflater-appcompat/src/main/AndroidManifest.xml b/asynclayoutinflater/asynclayoutinflater-appcompat/src/main/AndroidManifest.xml
deleted file mode 100644
index 2d12952..0000000
--- a/asynclayoutinflater/asynclayoutinflater-appcompat/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/asynclayoutinflater/asynclayoutinflater/src/main/AndroidManifest.xml b/asynclayoutinflater/asynclayoutinflater/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/asynclayoutinflater/asynclayoutinflater/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/autofill/autofill/src/main/AndroidManifest.xml b/autofill/autofill/src/main/AndroidManifest.xml
deleted file mode 100644
index 19e768b..0000000
--- a/autofill/autofill/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
\ No newline at end of file
diff --git a/benchmark/benchmark-common/api/public_plus_experimental_current.txt b/benchmark/benchmark-common/api/public_plus_experimental_current.txt
index a5a3dc8..c948ed9 100644
--- a/benchmark/benchmark-common/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-common/api/public_plus_experimental_current.txt
@@ -31,6 +31,21 @@
   @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoCaptureApi {
   }
 
+  @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public abstract sealed class PerfettoConfig {
+  }
+
+  public static final class PerfettoConfig.Binary extends androidx.benchmark.perfetto.PerfettoConfig {
+    ctor public PerfettoConfig.Binary(byte[] bytes);
+    method public byte[] getBytes();
+    property public final byte[] bytes;
+  }
+
+  public static final class PerfettoConfig.Text extends androidx.benchmark.perfetto.PerfettoConfig {
+    ctor public PerfettoConfig.Text(String text);
+    method public String getText();
+    property public final String text;
+  }
+
   @RequiresApi(23) @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTrace {
     ctor public PerfettoTrace(String path);
     method public String getPath();
@@ -38,6 +53,10 @@
     method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     property public final String path;
     field public static final androidx.benchmark.perfetto.PerfettoTrace.Companion Companion;
   }
@@ -47,6 +66,10 @@
     method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
 }
diff --git a/benchmark/benchmark-common/build.gradle b/benchmark/benchmark-common/build.gradle
index ab830a8..e8f2eca 100644
--- a/benchmark/benchmark-common/build.gradle
+++ b/benchmark/benchmark-common/build.gradle
@@ -72,6 +72,15 @@
     description = "Android Benchmark - Common"
 }
 
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        // Enable using experimental APIs from within same version group
+        freeCompilerArgs += [
+                "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi"
+        ]
+    }
+}
+
 // https://github.com/square/wire/issues/1947
 // Remove when we upgrade to fixed wire library
 afterEvaluate {
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
index 3164fcc..737b39d 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -29,6 +29,7 @@
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Assume.assumeTrue
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -189,7 +190,7 @@
         assumeTrue(
             CpuInfo.locked ||
                 IsolationActivity.sustainedPerformanceModeInUse ||
-                Errors.isEmulator
+                DeviceInfo.isEmulator
         )
         iterationCheck(checkingForThermalThrottling = false)
     }
@@ -380,6 +381,7 @@
     }
 
     @Test
+    @Ignore("b/278737712")
     fun experimentalIters() {
         validateIters(
             warmupCount = 1,
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
index 95b10d3..a919048 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
@@ -17,6 +17,7 @@
 package androidx.benchmark
 
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.LOWEST_BUNDLED_VERSION_SUPPORTED
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -52,7 +53,7 @@
 
         // start perfetto
         val capture = PerfettoCapture(unbundled)
-        capture.start(listOf(Packages.TEST))
+        capture.start(PerfettoConfig.Benchmark(listOf(Packages.TEST)))
 
         // should be at least one perfetto process
         assertNotEquals(illegal = listOf(), actual = getPerfettoPids())
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
index 8eba619..affb068 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
@@ -17,13 +17,19 @@
 package androidx.benchmark
 
 import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.PerfettoTrace
+import androidx.benchmark.perfetto.perfettoConfig
+import androidx.benchmark.perfetto.validateAndEncode
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
 import kotlin.test.assertFailsWith
 import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
 import kotlin.test.fail
 import org.junit.Assume.assumeTrue
 import org.junit.Test
@@ -34,6 +40,7 @@
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = 23)
 class PerfettoTraceTest {
+
     @Test
     fun record_basic() {
         assumeTrue(PerfettoHelper.isAbiSupported())
@@ -51,6 +58,94 @@
             "$perfettoTrace didn't match!"
         }
     }
+
+    private fun verifyRecordSuccess(
+        config: PerfettoConfig
+    ) {
+        var perfettoTrace: PerfettoTrace? = null
+        val label = "successTrace${config.javaClass.simpleName}"
+        PerfettoTrace.record(
+            fileLabel = label,
+            config = config,
+            traceCallback = { trace ->
+                perfettoTrace = trace
+            }
+        ) {
+            // noop
+        }
+        assertNotNull(perfettoTrace)
+        assert(perfettoTrace!!.path.matches(Regex(".*/${label}_[0-9-]+.perfetto-trace"))) {
+            "$perfettoTrace didn't match!"
+        }
+    }
+
+    private fun verifyRecordFails(
+        config: PerfettoConfig
+    ) {
+        var perfettoTrace: PerfettoTrace? = null
+        val exception = assertFailsWith<IllegalStateException> {
+            PerfettoTrace.record(
+                fileLabel = "failTrace",
+                config = config,
+                traceCallback = { trace ->
+                    perfettoTrace = trace
+                }
+            ) {
+                // noop
+            }
+        }
+        assertTrue(exception.message!!.contains("Perfetto unexpected exit code"))
+        assertNull(perfettoTrace)
+    }
+
+    @Test
+    fun record_invalidText() = verifyRecordFails(PerfettoConfig.Text("INVALID"))
+
+    @Test
+    fun record_invalidBinary() = verifyRecordFails(PerfettoConfig.Binary(byteArrayOf(1, 0, 1)))
+
+    @Test
+    fun record_validText() = verifyRecordSuccess(PerfettoConfig.Text("""
+        # basic config generated from https://ui.perfetto.dev/#!/record
+        buffers: {
+            size_kb: 63488
+            fill_policy: RING_BUFFER
+        }
+        buffers: {
+            size_kb: 2048
+            fill_policy: RING_BUFFER
+        }
+        data_sources: {
+            config {
+                name: "linux.ftrace"
+                ftrace_config {
+                    ftrace_events: "ftrace/print"
+                    atrace_categories: "am"
+                    atrace_categories: "dalvik"
+                    atrace_categories: "gfx"
+                    atrace_categories: "view"
+                    atrace_categories: "wm"
+                }
+            }
+        }
+        duration_ms: 10000
+        flush_period_ms: 30000
+        incremental_state_config {
+            clear_period_ms: 5000
+        }
+    """.trimIndent()))
+
+    @Test
+    fun record_validBinary() = verifyRecordSuccess(
+        PerfettoConfig.Binary(
+            perfettoConfig(
+                atraceApps = listOf(
+                    InstrumentationRegistry.getInstrumentation().targetContext.packageName
+                )
+            ).validateAndEncode()
+        )
+    )
+
     @Test
     fun record_reentrant() {
         assumeTrue(PerfettoHelper.isAbiSupported())
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
index b336117..19d5d66 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
@@ -32,8 +32,7 @@
         Build.MODEL.contains("google_sdk") ||
         Build.MODEL.contains("sdk_gphone64") ||
         Build.MODEL.contains("Emulator") ||
-        Build.MODEL.contains("Android SDK built for x86") ||
-        Build.MODEL.contains("Android SDK built for arm64") ||
+        Build.MODEL.contains("Android SDK built for") ||
         Build.MANUFACTURER.contains("Genymotion") ||
         Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic") ||
         "google_sdk" == Build.PRODUCT
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Errors.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Errors.kt
index 9919011..6403312 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Errors.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Errors.kt
@@ -64,15 +64,6 @@
         return ret
     }
 
-    val isEmulator = Build.FINGERPRINT.startsWith("generic") ||
-        Build.FINGERPRINT.startsWith("unknown") ||
-        Build.MODEL.contains("google_sdk") ||
-        Build.MODEL.contains("Emulator") ||
-        Build.MODEL.contains("Android SDK built for x86") ||
-        Build.MANUFACTURER.contains("Genymotion") ||
-        Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic") ||
-        "google_sdk" == Build.PRODUCT
-
     private val isDeviceRooted =
         arrayOf(
             "/system/app/Superuser.apk",
@@ -110,7 +101,7 @@
                 |    real user's experience (or even regress release performance).
             """.trimMarginWrapNewlines()
         }
-        if (isEmulator) {
+        if (DeviceInfo.isEmulator) {
             warningPrefix += "EMULATOR_"
             warningString += """
                 |WARNING: Running on Emulator
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/ThrottleDetector.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ThrottleDetector.kt
index e6fcaba..a074e20 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/ThrottleDetector.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ThrottleDetector.kt
@@ -64,7 +64,7 @@
             initNs == 0.0 && // first time
             !CpuInfo.locked && // CPU locked (presumably to stable values), should be no throttling
             !IsolationActivity.sustainedPerformanceModeInUse && // trust sustained perf
-            !Errors.isEmulator // don't bother with emulators, will always be unpredicatable
+            !DeviceInfo.isEmulator // don't bother with emulators, will always be unpredictable
         ) {
             initNs = measureWorkNs()
         }
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index 26a4da0..e99d5a1 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -53,31 +53,24 @@
 
     private val helper: PerfettoHelper = PerfettoHelper(unbundled)
 
-    public fun isRunning() = helper.isRunning()
+    fun isRunning() = helper.isRunning()
 
     /**
      * Start collecting perfetto trace.
-     *
-     * TODO: provide configuration options
      */
-    public fun start(packages: List<String>) = userspaceTrace("start perfetto") {
-        // Write binary proto to dir that shell can read
-        // TODO: cache on disk
+    fun start(config: PerfettoConfig) = userspaceTrace("start perfetto") {
+        // Write config proto to dir that shell can read
+        //     We use `.pb` even with textproto so we'll only ever have one file
         val configProtoFile = File(Outputs.dirUsableByAppAndShell, "trace_config.pb")
         try {
             userspaceTrace("write config") {
-                val atraceApps = if (Build.VERSION.SDK_INT <= 28 || packages.isEmpty()) {
-                    packages
-                } else {
-                    listOf("*")
-                }
-                configProtoFile.writeBytes(perfettoConfig(atraceApps).validateAndEncode())
+                config.writeTo(configProtoFile)
                 if (Outputs.forceFilesForShellAccessible) {
                     configProtoFile.setReadable(true, /* ownerOnly = */ false)
                 }
             }
             userspaceTrace("start perfetto process") {
-                helper.startCollecting(configProtoFile.absolutePath, false)
+                helper.startCollecting(configProtoFile.absolutePath, config.isTextProto)
             }
         } finally {
             configProtoFile.delete()
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 010cde4..bb1e825 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -54,23 +54,21 @@
 
     @RequiresApi(23)
     private fun start(
-        appTagPackages: List<String>,
+        config: PerfettoConfig,
         userspaceTracingPackage: String?
     ): Boolean {
         capture?.apply {
-            if (Build.VERSION.SDK_INT >= 23) {
-                Log.d(LOG_TAG, "Recording perfetto trace")
-                if (userspaceTracingPackage != null &&
-                    Build.VERSION.SDK_INT >= 30
-                ) {
-                    val result = enableAndroidxTracingPerfetto(
-                        targetPackage = userspaceTracingPackage,
-                        provideBinariesIfMissing = true
-                    ) ?: "Success"
-                    Log.d(LOG_TAG, "Enable full tracing result=$result")
-                }
-                start(appTagPackages)
+            Log.d(LOG_TAG, "Recording perfetto trace")
+            if (userspaceTracingPackage != null &&
+                Build.VERSION.SDK_INT >= 30
+            ) {
+                val result = enableAndroidxTracingPerfetto(
+                    targetPackage = userspaceTracingPackage,
+                    provideBinariesIfMissing = true
+                ) ?: "Success"
+                Log.d(LOG_TAG, "Enable full tracing result=$result")
             }
+            start(config)
         }
 
         return true
@@ -94,7 +92,7 @@
 
     fun record(
         fileLabel: String,
-        appTagPackages: List<String>,
+        config: PerfettoConfig,
         userspaceTracingPackage: String?,
         traceCallback: ((String) -> Unit)? = null,
         block: () -> Unit
@@ -125,7 +123,7 @@
         val path: String
         try {
             propOverride?.forceValue()
-            start(appTagPackages, userspaceTracingPackage)
+            start(config, userspaceTracingPackage)
             try {
                 block()
             } finally {
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
index 6a2283e..2ea9b46 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
@@ -20,6 +20,7 @@
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.benchmark.Shell
+import java.io.File
 import perfetto.protos.DataSourceConfig
 import perfetto.protos.AndroidPowerConfig
 import perfetto.protos.FtraceConfig
@@ -30,6 +31,73 @@
 import perfetto.protos.TraceConfig.BufferConfig
 import perfetto.protos.TraceConfig.BufferConfig.FillPolicy
 
+/**
+ * Configuration for Perfetto trace recording.
+ *
+ * For more info, see https://perfetto.dev/docs/concepts/config
+ */
+@ExperimentalPerfettoCaptureApi
+sealed class PerfettoConfig constructor(
+    internal val isTextProto: Boolean
+) {
+    @RequiresApi(23)
+    internal abstract fun writeTo(file: File)
+
+    /**
+     * Binary representation of a Perfetto config proto.
+     *
+     * This can be generated by a proto library, together with the definition here:
+     */
+    class Binary(
+        val bytes: ByteArray
+    ) : PerfettoConfig(isTextProto = false) {
+        @RequiresApi(23)
+        override fun writeTo(file: File) {
+            file.writeBytes(bytes)
+        }
+    }
+
+    /**
+     * TextProto representation of a Perfetto config.
+     *
+     * This can be generated with https://ui.perfetto.dev/#!/record/
+     *
+     * Note: this format is not recommended for long term use - the [Binary] proto
+     * representation is more likely to remain stable over time, across Perfetto/Android OS
+     * versions. For more information, see
+     * [the Perfetto documentation](https://perfetto.dev/docs/concepts/config#pbtx-vs-binary-format).
+     */
+    class Text(
+        val text: String
+    ) : PerfettoConfig(isTextProto = true) {
+        @RequiresApi(23)
+        override fun writeTo(file: File) {
+            file.writeText(text)
+        }
+    }
+
+    /**
+     * Benchmark defined config for perfetto trace capture, used by benchmark/macrobenchmark.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    class Benchmark(
+        private val appTagPackages: List<String>
+    ) : PerfettoConfig(isTextProto = false) {
+        @RequiresApi(23)
+        override fun writeTo(file: File) {
+            file.writeBytes(
+                perfettoConfig(
+                    atraceApps = if (Build.VERSION.SDK_INT <= 28 || appTagPackages.isEmpty()) {
+                        appTagPackages
+                    } else {
+                        listOf("*")
+                    }
+                ).validateAndEncode()
+            )
+        }
+    }
+}
+
 private fun ftraceDataSource(
     atraceApps: List<String>
 ) = TraceConfig.DataSource(
@@ -165,7 +233,7 @@
  * @suppress
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun perfettoConfig(
+internal fun perfettoConfig(
     atraceApps: List<String>
 ) = TraceConfig(
     buffers = listOf(
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
index 5e2c29a..656ba2f 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
@@ -83,15 +83,79 @@
              * Block to be traced.
              */
             block: () -> Unit
+        ) = record(
+            fileLabel = fileLabel,
+            config = PerfettoConfig.Benchmark(appTagPackages),
+            userspaceTracingPackage = userspaceTracingPackage,
+            traceCallback = traceCallback,
+            block = block
+        )
+
+        /**
+         * Record a Perfetto System Trace for the specified [block], with a fully custom Perfetto
+         * config, either text or binary.
+         *
+         * ```
+         * PerfettoTrace.record("myTrace", config = """...""") {
+         *     // content in here is traced to myTrace_<timestamp>.perfetto_trace
+         * }
+         * ```
+         *
+         * Reentrant Perfetto trace capture is not supported, so this API may not be combined with
+         * `BenchmarkRule`, `MacrobenchmarkRule`, or `PerfettoTraceRule`.
+         *
+         * If the block throws, the trace is still captured and passed to [traceCallback].
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun record(
+            /**
+             * Output trace file names are labelled `<fileLabel>_<timestamp>.perfetto_trace`
+             *
+             * This timestamp is used for uniqueness when trace files are pulled automatically to
+             * Studio.
+             */
+            fileLabel: String,
+            /**
+             * Trace recording configuration.
+             */
+            config: PerfettoConfig,
+            /**
+             * Process to emphasize in the tracing UI.
+             *
+             * Used to emphasize the target process, e.g. by pre-populating Studio trace viewer
+             * process selection.
+             *
+             * Defaults to the test's target process. Note that for self-instrumenting tests that
+             * measure another app, you must pass that target app package.
+             */
+            highlightPackage: String =
+                InstrumentationRegistry.getInstrumentation().targetContext.packageName,
+            /**
+             * Process to trace with userspace tracing, i.e. `androidx.tracing:tracing-perfetto`,
+             * ignored below API 30.
+             *
+             * This tracing is lower overhead than standard `android.os.Trace` tracepoints, but is
+             * currently experimental.
+             */
+            userspaceTracingPackage: String? = null,
+            /**
+             * Callback for trace capture.
+             *
+             * This callback allows you to process the trace even if the block throws, e.g. during
+             * a test failure.
+             */
+            traceCallback: ((PerfettoTrace) -> Unit)? = null,
+            /**
+             * Block to be traced.
+             */
+            block: () -> Unit
         ) {
             PerfettoCaptureWrapper().record(
                 fileLabel = fileLabel,
-                appTagPackages = appTagPackages,
-                userspaceTracingPackage = userspaceTracingPackage,
+                config,
+                userspaceTracingPackage,
                 traceCallback = { path ->
-                    // emphasize the first package in the package list, or target package otherwise
-                    val highlightPackage = appTagPackages.firstOrNull()
-                        ?: InstrumentationRegistry.getInstrumentation().targetContext.packageName
                     File(path).appendUiState(
                         UiState(
                             timelineStart = null,
diff --git a/benchmark/benchmark-junit4/build.gradle b/benchmark/benchmark-junit4/build.gradle
index baf3b33..4db5a73 100644
--- a/benchmark/benchmark-junit4/build.gradle
+++ b/benchmark/benchmark-junit4/build.gradle
@@ -15,6 +15,7 @@
  */
 
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -51,3 +52,12 @@
     inceptionYear = "2019"
     description = "Android Benchmark - JUnit4"
 }
+
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        // Enable using experimental APIs from within same version group
+        freeCompilerArgs += [
+                "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi"
+        ]
+    }
+}
diff --git a/benchmark/benchmark-junit4/src/main/AndroidManifest.xml b/benchmark/benchmark-junit4/src/main/AndroidManifest.xml
deleted file mode 100644
index cf0d807..0000000
--- a/benchmark/benchmark-junit4/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
index 1be0375..0c686fe 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
@@ -23,6 +23,7 @@
 import androidx.benchmark.BenchmarkState
 import androidx.benchmark.UserspaceTracing
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.UiState
 import androidx.benchmark.perfetto.appendUiState
 import androidx.test.platform.app.InstrumentationRegistry
@@ -212,7 +213,7 @@
 
             val tracePath = PerfettoCaptureWrapper().record(
                 fileLabel = uniqueName,
-                appTagPackages = packages,
+                config = PerfettoConfig.Benchmark(packages),
                 userspaceTracingPackage = null
             ) {
                 UserspaceTracing.commitToTrace() // clear buffer
diff --git a/benchmark/benchmark-macro-junit4/src/main/AndroidManifest.xml b/benchmark/benchmark-macro-junit4/src/main/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/benchmark/benchmark-macro-junit4/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
index 8a0d906..c6dd32b 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
@@ -55,6 +55,7 @@
  *         startActivityAndWait()
  *     }
  * }
+ * ```
  *
  * Note that you can filter captured rules, for example, if you're generating rules for a library,
  * and don't want to record profiles from outside that library:
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index a6b3f2f..0bb6d0b 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -150,10 +150,10 @@
 // https://github.com/square/wire/issues/1947
 // Remove when we upgrade to fixed wire library
 afterEvaluate {
-    tasks.named("compileReleaseKotlin").configure {
+    tasks.named("compileDebugKotlin").configure {
         it.dependsOn("generateDebugProtos")
     }
-    tasks.named("compileDebugKotlin").configure {
+    tasks.named("compileReleaseKotlin").configure {
         it.dependsOn("generateReleaseProtos")
     }
 }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
index 9e98054..cf8d947 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
@@ -23,6 +23,7 @@
 import androidx.benchmark.DeviceInfo
 import androidx.benchmark.Outputs
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -48,6 +49,7 @@
 class StartupTimingMetricTest {
     @MediumTest
     @Test
+    @Ignore("b/258335082")
     fun noResults() {
         assumeTrue(isAbiSupported())
         val packageName = "fake.package.fiction.nostartups"
@@ -170,12 +172,14 @@
 
     @LargeTest
     @Test
+    @Ignore("b/258335082")
     fun startup_fullyDrawn_immediate() {
         validateStartup_fullyDrawn(delayMs = 0)
     }
 
     @LargeTest
     @Test
+    @Ignore("b/258335082")
     fun startup_fullyDrawn_delayed() {
         validateStartup_fullyDrawn(delayMs = 100)
     }
@@ -290,13 +294,16 @@
     metric.configure(packageName)
     val tracePath = PerfettoCaptureWrapper().record(
         fileLabel = packageName,
-        // note - packageName may be this package, so we convert to set then list to make unique
-        // and on API 23 and below, we use reflection to trace instead within this process
-        appTagPackages = if (Build.VERSION.SDK_INT >= 24 && packageName != Packages.TEST) {
-            listOf(packageName, Packages.TEST)
-        } else {
-            listOf(packageName)
-        },
+        config = PerfettoConfig.Benchmark(
+            // note - packageName may be this package, so we convert to set then list to make unique
+            // and on API 23 and below, we use reflection to trace instead within this process
+            appTagPackages = if (Build.VERSION.SDK_INT >= 24 && packageName != Packages.TEST) {
+                listOf(packageName, Packages.TEST)
+            } else {
+                listOf(packageName)
+            },
+
+        ),
         userspaceTracingPackage = packageName,
         block = measureBlock
     )!!
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AndroidxTracingTraceTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AndroidxTracingTraceTest.kt
index 657a201..ec0e17c 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AndroidxTracingTraceTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AndroidxTracingTraceTest.kt
@@ -19,6 +19,7 @@
 import androidx.benchmark.macro.FileLinkingRule
 import androidx.benchmark.macro.Packages
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
@@ -66,7 +67,7 @@
 
         verifyTraceEnable(false)
 
-        perfettoCapture.start(listOf(Packages.TEST))
+        perfettoCapture.start(PerfettoConfig.Benchmark(listOf(Packages.TEST)))
 
         assertTrue(
             Trace.isEnabled(),
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
index 8797a72..a8fb5bf 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
@@ -16,10 +16,12 @@
 
 package androidx.benchmark.macro.perfetto
 
+import android.annotation.SuppressLint
 import android.os.Build
 import androidx.benchmark.macro.FileLinkingRule
 import androidx.benchmark.macro.Packages
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.LOWEST_BUNDLED_VERSION_SUPPORTED
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
@@ -85,6 +87,7 @@
         captureAndValidateTrace(unbundled = true)
     }
 
+    @SuppressLint("BanThreadSleep")
     private fun captureAndValidateTrace(unbundled: Boolean) {
         assumeTrue(isAbiSupported())
 
@@ -93,7 +96,7 @@
 
         verifyTraceEnable(false)
 
-        perfettoCapture.start(listOf(Packages.TEST))
+        perfettoCapture.start(PerfettoConfig.Benchmark(listOf(Packages.TEST)))
 
         if (!Trace.isEnabled()) {
             // Should be available immediately, but let's wait a while to see if it works slowly.
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index c220445..2a4020b 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -33,6 +33,7 @@
 import androidx.benchmark.checkAndGetSuppressionState
 import androidx.benchmark.conditionalError
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
+import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoTrace
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
 import androidx.benchmark.perfetto.UiState
@@ -214,22 +215,23 @@
                 val iterString = iteration.toString().padStart(3, '0')
                 val tracePath = perfettoCollector.record(
                     fileLabel = "${uniqueName}_iter$iterString",
-
-                    /**
-                     * Prior to API 24, every package name was joined into a single setprop which
-                     * can overflow, and disable *ALL* app level tracing.
-                     *
-                     * For safety here, we only trace the macrobench package on newer platforms,
-                     * and use reflection in the macrobench test process to trace important
-                     * sections
-                     *
-                     * @see androidx.benchmark.macro.perfetto.ForceTracing
-                     */
-                    appTagPackages = if (Build.VERSION.SDK_INT >= 24) {
-                        listOf(packageName, macrobenchPackageName)
-                    } else {
-                        listOf(packageName)
-                    },
+                    config = PerfettoConfig.Benchmark(
+                        /**
+                         * Prior to API 24, every package name was joined into a single setprop
+                         * which can overflow, and disable *ALL* app level tracing.
+                         *
+                         * For safety here, we only trace the macrobench package on newer platforms,
+                         * and use reflection in the macrobench test process to trace important
+                         * sections
+                         *
+                         * @see androidx.benchmark.macro.perfetto.ForceTracing
+                         */
+                        appTagPackages = if (Build.VERSION.SDK_INT >= 24) {
+                            listOf(packageName, macrobenchPackageName)
+                        } else {
+                            listOf(packageName)
+                        },
+                    ),
                     userspaceTracingPackage = userspaceTracingPackage
                 ) {
                     try {
diff --git a/benchmark/integration-tests/baselineprofile-flavors-producer/src/free/AndroidManifest.xml b/benchmark/integration-tests/baselineprofile-flavors-producer/src/free/AndroidManifest.xml
deleted file mode 100644
index bae036b..0000000
--- a/benchmark/integration-tests/baselineprofile-flavors-producer/src/free/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/baselineprofile-flavors-producer/src/main/AndroidManifest.xml b/benchmark/integration-tests/baselineprofile-flavors-producer/src/main/AndroidManifest.xml
deleted file mode 100644
index d4c1970..0000000
--- a/benchmark/integration-tests/baselineprofile-flavors-producer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/baselineprofile-flavors-producer/src/paid/AndroidManifest.xml b/benchmark/integration-tests/baselineprofile-flavors-producer/src/paid/AndroidManifest.xml
deleted file mode 100644
index bae036b..0000000
--- a/benchmark/integration-tests/baselineprofile-flavors-producer/src/paid/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/baselineprofile-library-consumer/src/main/AndroidManifest.xml b/benchmark/integration-tests/baselineprofile-library-consumer/src/main/AndroidManifest.xml
deleted file mode 100644
index 7c52910..0000000
--- a/benchmark/integration-tests/baselineprofile-library-consumer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  ~ Copyright 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/baselineprofile-library-producer/src/main/AndroidManifest.xml b/benchmark/integration-tests/baselineprofile-library-producer/src/main/AndroidManifest.xml
deleted file mode 100644
index bae036b..0000000
--- a/benchmark/integration-tests/baselineprofile-library-producer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/baselineprofile-producer/src/main/AndroidManifest.xml b/benchmark/integration-tests/baselineprofile-producer/src/main/AndroidManifest.xml
deleted file mode 100644
index bae036b..0000000
--- a/benchmark/integration-tests/baselineprofile-producer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/benchmark/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/benchmark/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index ed173d5..0000000
--- a/benchmark/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/biometric/biometric-ktx/src/main/AndroidManifest.xml b/biometric/biometric-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 227314e..0000000
--- a/biometric/biometric-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-<manifest />
\ No newline at end of file
diff --git a/bluetooth/bluetooth-testing/src/androidTest/AndroidManifest.xml b/bluetooth/bluetooth-testing/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index a9bb124..0000000
--- a/bluetooth/bluetooth-testing/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/bluetooth/bluetooth-testing/src/main/AndroidManifest.xml b/bluetooth/bluetooth-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index 9ac1b77..0000000
--- a/bluetooth/bluetooth-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2022 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/bluetooth/integration-tests/testapp/build.gradle b/bluetooth/integration-tests/testapp/build.gradle
index 55523d6..c99f4b1 100644
--- a/bluetooth/integration-tests/testapp/build.gradle
+++ b/bluetooth/integration-tests/testapp/build.gradle
@@ -50,7 +50,7 @@
     implementation("androidx.appcompat:appcompat:1.6.1")
     implementation(libs.constraintLayout)
     implementation("androidx.core:core-ktx:1.10.0")
-    implementation("androidx.fragment:fragment-ktx:1.5.6")
+    implementation("androidx.fragment:fragment-ktx:1.5.7")
     implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
     implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
     implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt
index 593f46b..f4e7d85 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt
@@ -66,11 +66,7 @@
         container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View {
-        Log.d(
-            TAG, "onCreateView() called with: inflater = $inflater, " +
-                "container = $container, savedInstanceState = $savedInstanceState"
-        )
-        mHomeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
+        mHomeViewModel = ViewModelProvider(this)[HomeViewModel::class.java]
 
         _binding = FragmentHomeBinding.inflate(inflater, container, false)
         return binding.root
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt
index 4d85a68..88b1c1c 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt
@@ -21,7 +21,7 @@
 
 class HomeViewModel : ViewModel() {
 
-    companion object {
+    private companion object {
         private const val TAG = "HomeViewModel"
     }
 
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
index dc9536e..ca0309d 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
@@ -16,10 +16,6 @@
 
 package androidx.bluetooth.integration.testapp.ui.scanner
 
-// TODO(ofy) Migrate from androidx.bluetooth.integration.testapp.experimental.BluetoothLe to
-// androidx.bluetooth.BluetoothDevice once in place
-// TODO(ofy) Migrate from androidx.bluetooth.integration.testapp.experimental.BluetoothLe to
-// androidx.bluetooth.BluetoothLe once scan API is in place
 import android.bluetooth.le.ScanResult
 import android.bluetooth.le.ScanSettings
 import android.os.Bundle
@@ -32,6 +28,7 @@
 import androidx.bluetooth.integration.testapp.R
 import androidx.bluetooth.integration.testapp.databinding.FragmentScannerBinding
 import android.annotation.SuppressLint
+// TODO(ofy) Migrate to androidx.bluetooth.BluetoothLe once scan API is in place
 import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
 import androidx.bluetooth.integration.testapp.ui.common.getColor
 import androidx.core.view.isVisible
@@ -138,8 +135,6 @@
             DividerItemDecoration(context, LinearLayoutManager.VERTICAL)
         )
 
-        initData()
-
         binding.buttonScan.setOnClickListener {
             if (scanJob?.isActive == true) {
                 isScanning = false
@@ -148,6 +143,8 @@
             }
         }
 
+        initData()
+
         return binding.root
     }
 
@@ -155,8 +152,6 @@
         super.onDestroyView()
         _binding = null
         isScanning = false
-        scanJob?.cancel()
-        scanJob = null
     }
 
     private fun initData() {
@@ -197,7 +192,7 @@
             binding.tabLayout.getTabAt(index)
         }
 
-        // To prevent TabSelectedListener being triggered when a tab is promatically selected.
+        // To prevent TabSelectedListener being triggered when a tab is programmatically selected.
         binding.tabLayout.removeOnTabSelectedListener(onTabSelectedListener)
         binding.tabLayout.selectTab(deviceTab)
         binding.tabLayout.addOnTabSelectedListener(onTabSelectedListener)
@@ -217,7 +212,9 @@
 
         val customView = newTab.customView
         customView?.findViewById<TextView>(R.id.text_view_address)?.text = deviceAddress
-        customView?.findViewById<TextView>(R.id.text_view_name)?.text = deviceName
+        val textViewName = customView?.findViewById<TextView>(R.id.text_view_name)
+        textViewName?.text = deviceName
+        textViewName?.isVisible = deviceName.isNullOrEmpty().not()
 
         binding.tabLayout.addTab(newTab)
         return newTab
diff --git a/browser/browser/src/main/AndroidManifest.xml b/browser/browser/src/main/AndroidManifest.xml
deleted file mode 100644
index 3e475b2..0000000
--- a/browser/browser/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index 32f63ba..c596da5 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -153,6 +153,7 @@
                     error.add("ComposableLambdaParameterPosition")
                     error.add("CompositionLocalNaming")
                     error.add("ComposableModifierFactory")
+                    error.add("AutoboxingStateCreation")
                     error.add("AutoboxingStateValueProperty")
                     error.add("InvalidColorHexValue")
                     error.add("MissingColorAlphaChannel")
@@ -161,6 +162,7 @@
                     error.add("ModifierNodeInspectableProperties")
                     error.add("ModifierParameter")
                     error.add("MutableCollectionMutableState")
+                    error.add("OpaqueUnitKey")
                     error.add("UnnecessaryComposedModifier")
                     error.add("FrequentlyChangedStateReadInComposition")
                     error.add("ReturnFromAwaitPointerEventScope")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index f868506..3fae48c 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -29,7 +29,6 @@
 import androidx.build.checkapi.LibraryApiTaskConfig
 import androidx.build.checkapi.configureProjectForApiTasks
 import androidx.build.dependencies.KOTLIN_VERSION
-import androidx.build.dependencyTracker.AffectedModuleDetector
 import androidx.build.docs.AndroidXKmpDocsImplPlugin
 import androidx.build.gradle.isRoot
 import androidx.build.license.configureExternalDependencyLicenseCheck
@@ -37,7 +36,6 @@
 import androidx.build.sbom.validateAllArchiveInputsRecognized
 import androidx.build.studio.StudioTask
 import androidx.build.testConfiguration.addAppApkToTestConfigGeneration
-import androidx.build.testConfiguration.addToTestZips
 import androidx.build.testConfiguration.configureTestConfigGeneration
 import com.android.build.api.artifact.SingleArtifact
 import com.android.build.api.dsl.ManagedVirtualDevice
@@ -54,7 +52,6 @@
 import com.android.build.gradle.TestExtension
 import com.android.build.gradle.TestPlugin
 import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.internal.tasks.ListingFileRedirectTask
 import java.io.File
 import java.time.Duration
 import java.util.Locale
@@ -702,19 +699,6 @@
         project.configureTestConfigGeneration(this)
         project.configureFtlRunner()
 
-        val buildTestApksTask = project.rootProject.tasks.named(BUILD_TEST_APKS_TASK)
-        when (this) {
-            is TestedExtension -> testVariants
-            // app module defines variants for test module
-            is TestExtension -> applicationVariants
-            else -> throw IllegalStateException("Unsupported plugin type")
-        }.all { variant ->
-            buildTestApksTask.configure {
-                it.dependsOn(variant.assembleProvider)
-            }
-            variant.configureApkZipping(project)
-        }
-
         // AGP warns if we use project.buildDir (or subdirs) for CMake's generated
         // build files (ninja build files, CMakeCache.txt, etc.). Use a staging directory that
         // lives alongside the project's buildDir.
@@ -722,24 +706,6 @@
             File(project.buildDir, "../nativeBuildStaging")
     }
 
-    /**
-     * Configures the ZIP_TEST_CONFIGS_WITH_APKS_TASK to include the test apk if applicable
-     */
-    @Suppress("DEPRECATION") // ApkVariant
-    private fun com.android.build.gradle.api.ApkVariant.configureApkZipping(
-        project: Project
-    ) {
-        packageApplicationProvider.get().let { packageTask ->
-            AffectedModuleDetector.configureTaskGuard(packageTask)
-            addToTestZips(project, packageTask)
-        }
-        // This task needs to be guarded by AffectedModuleDetector due to guarding test
-        // APK building above. It can only be removed if we stop using AMD for test APKs.
-        project.tasks.withType(ListingFileRedirectTask::class.java).forEach {
-            AffectedModuleDetector.configureTaskGuard(it)
-        }
-    }
-
     private fun LibraryExtension.configureAndroidLibraryOptions(
         project: Project,
         androidXExtension: AndroidXExtension
@@ -850,17 +816,6 @@
 
         project.addAppApkToTestConfigGeneration()
         project.addAppApkToFtlRunner()
-
-        val buildTestApksTask = project.rootProject.tasks.named(BUILD_TEST_APKS_TASK)
-        applicationVariants.all { variant ->
-            // Using getName() instead of name due to b/150427408
-            if (variant.buildType.name == "debug") {
-                buildTestApksTask.configure {
-                    it.dependsOn(variant.assembleProvider)
-                }
-            }
-            variant.configureApkZipping(project)
-        }
     }
 
     private fun Project.configureDependencyVerification(
@@ -1001,7 +956,6 @@
     }
 
     companion object {
-        const val BUILD_TEST_APKS_TASK = "buildTestApks"
         const val CREATE_LIBRARY_BUILD_INFO_FILES_TASK = "createLibraryBuildInfoFiles"
         const val GENERATE_TEST_CONFIGURATION_TASK = "GenerateTestConfiguration"
         const val ZIP_TEST_CONFIGS_WITH_APKS_TASK = "zipTestConfigsWithApks"
@@ -1097,14 +1051,6 @@
     }
 }
 
-/**
- * Guarantees unique names for the APKs, and modifies some of the suffixes. The APK name is used
- * to determine what gets run by our test runner
- */
-fun String.renameApkForTesting(projectPath: String): String {
-    return "${projectPath.asFilenamePrefix()}_$this"
-}
-
 fun Project.hasBenchmarkPlugin(): Boolean {
     return this.plugins.hasPlugin(BenchmarkPlugin::class.java)
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index f7bd29a..7a4f3c0 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -136,8 +136,6 @@
             }
         }
 
-        tasks.register(AndroidXImplPlugin.BUILD_TEST_APKS_TASK)
-
         // NOTE: this task is used by the Github CI as well. If you make any changes here,
         // please update the .github/workflows files as well, if necessary.
         project.tasks.register(
@@ -150,6 +148,7 @@
             it.entryCompression = ZipEntryCompression.STORED
             // Archive is greater than 4Gb :O
             it.isZip64 = true
+            it.isReproducibleFileOrder = true
         }
         project.tasks.register(
             ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK, Zip::class.java
@@ -161,6 +160,7 @@
             it.entryCompression = ZipEntryCompression.STORED
             // Archive is greater than 4Gb :O
             it.isZip64 = true
+            it.isReproducibleFileOrder = true
         }
 
         AffectedModuleDetector.configure(gradle, this)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt
index 36ba7f9..57de24e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt
@@ -17,8 +17,6 @@
 package androidx.build.testConfiguration
 
 import androidx.build.dependencyTracker.ProjectSubset
-import androidx.build.renameApkForTesting
-import com.android.build.api.variant.BuiltArtifact
 import com.android.build.api.variant.BuiltArtifacts
 import com.android.build.api.variant.BuiltArtifactsLoader
 import java.io.File
@@ -79,18 +77,6 @@
     abstract val affectedModuleDetectorSubset: Property<ProjectSubset>
 
     @get:Input
-    abstract val clientToTPath: Property<String>
-
-    @get:Input
-    abstract val clientPreviousPath: Property<String>
-
-    @get:Input
-    abstract val serviceToTPath: Property<String>
-
-    @get:Input
-    abstract val servicePreviousPath: Property<String>
-
-    @get:Input
     abstract val minSdk: Property<Int>
 
     @get:Input
@@ -117,39 +103,60 @@
     @get:OutputFile
     abstract val jsonClientToTServiceToTServiceTests: RegularFileProperty
 
+    @get:OutputFile
+    abstract val previousClientApk: RegularFileProperty
+
+    @get:OutputFile
+    abstract val totClientApk: RegularFileProperty
+
+    @get:OutputFile
+    abstract val previousServiceApk: RegularFileProperty
+
+    @get:OutputFile
+    abstract val totServiceApk: RegularFileProperty
+
     @TaskAction
     fun generateAndroidTestZip() {
-        val clientToTApk = resolveApk(clientToTFolder, clientToTLoader)
-        val clientPreviousApk = resolveApk(clientPreviousFolder, clientPreviousLoader)
-        val serviceToTApk = resolveApk(serviceToTFolder, serviceToTLoader)
-        val servicePreviousApk = resolveApk(
-            servicePreviousFolder, servicePreviousLoader
+        val clientToTApk = totClientApk.get().asFile
+        val clientToTSha256 = copyApkAndGetSha256(clientToTFolder, clientToTLoader, clientToTApk)
+        val clientPreviousApk = previousClientApk.get().asFile
+        val clientPreviousSha256 = copyApkAndGetSha256(
+            clientPreviousFolder, clientPreviousLoader, clientPreviousApk
         )
+        val serviceToTApk = totServiceApk.get().asFile
+        val serviceToTSha256 = copyApkAndGetSha256(
+            serviceToTFolder, serviceToTLoader, serviceToTApk
+        )
+        val servicePreviousApk = previousServiceApk.get().asFile
+        val servicePreviousSha256 = copyApkAndGetSha256(
+            servicePreviousFolder, servicePreviousLoader, servicePreviousApk
+        )
+
         writeConfigFileContent(
-            clientApk = clientToTApk,
-            serviceApk = serviceToTApk,
-            clientPath = clientToTPath.get(),
-            servicePath = serviceToTPath.get(),
+            clientApkName = clientToTApk.name,
+            serviceApkName = serviceToTApk.name,
+            clientApkSha256 = clientToTSha256,
+            serviceApkSha256 = serviceToTSha256,
             jsonClientOutputFile = jsonClientToTServiceToTClientTests,
             jsonServiceOutputFile = jsonClientToTServiceToTServiceTests,
             isClientPrevious = false,
             isServicePrevious = false
         )
         writeConfigFileContent(
-            clientApk = clientToTApk,
-            serviceApk = servicePreviousApk,
-            clientPath = clientToTPath.get(),
-            servicePath = servicePreviousPath.get(),
+            clientApkName = clientToTApk.name,
+            serviceApkName = servicePreviousApk.name,
+            clientApkSha256 = clientToTSha256,
+            serviceApkSha256 = servicePreviousSha256,
             jsonClientOutputFile = jsonClientToTServicePreviousClientTests,
             jsonServiceOutputFile = jsonClientToTServicePreviousServiceTests,
             isClientPrevious = false,
             isServicePrevious = true
         )
         writeConfigFileContent(
-            clientApk = clientPreviousApk,
-            serviceApk = serviceToTApk,
-            clientPath = clientPreviousPath.get(),
-            servicePath = serviceToTPath.get(),
+            clientApkName = clientPreviousApk.name,
+            serviceApkName = serviceToTApk.name,
+            clientApkSha256 = clientPreviousSha256,
+            serviceApkSha256 = serviceToTSha256,
             jsonClientOutputFile = jsonClientPreviousServiceToTClientTests,
             jsonServiceOutputFile = jsonClientPreviousServiceToTServiceTests,
             isClientPrevious = true,
@@ -165,26 +172,27 @@
             ?: throw RuntimeException("Cannot load required APK for task: $name")
     }
 
-    private fun BuiltArtifact.resolveName(path: String): String {
-        return outputFile.substringAfterLast("/").renameApkForTesting(path)
+    private fun copyApkAndGetSha256(
+        apkFolder: DirectoryProperty,
+        apkLoader: Property<BuiltArtifactsLoader>,
+        destination: File
+    ): String {
+        val artifacts = apkLoader.get().load(apkFolder.get())
+            ?: throw RuntimeException("Cannot load required APK for task: $name")
+        File(artifacts.elements.single().outputFile).copyTo(destination, overwrite = true)
+        return sha256(destination)
     }
 
     private fun writeConfigFileContent(
-        clientApk: BuiltArtifacts,
-        serviceApk: BuiltArtifacts,
-        clientPath: String,
-        servicePath: String,
+        clientApkName: String,
+        serviceApkName: String,
+        clientApkSha256: String,
+        serviceApkSha256: String,
         jsonClientOutputFile: RegularFileProperty,
         jsonServiceOutputFile: RegularFileProperty,
         isClientPrevious: Boolean,
         isServicePrevious: Boolean,
     ) {
-        val clientBuiltArtifact = clientApk.elements.single()
-        val serviceBuiltArtifact = serviceApk.elements.single()
-        val clientApkName = clientBuiltArtifact.resolveName(clientPath)
-        val clientApkSha256 = sha256(File(clientBuiltArtifact.outputFile))
-        val serviceApkName = serviceBuiltArtifact.resolveName(servicePath)
-        val serviceApkSha256 = sha256(File(serviceBuiltArtifact.outputFile))
         createOrFail(jsonClientOutputFile).writeText(
             buildMediaJson(
                 configName = jsonClientOutputFile.asFile.get().name,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
index 64ce692..1960148 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -17,15 +17,14 @@
 package androidx.build.testConfiguration
 
 import androidx.build.dependencyTracker.ProjectSubset
-import androidx.build.renameApkForTesting
 import com.android.build.api.variant.BuiltArtifactsLoader
 import java.io.File
 import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
 import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
-import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Internal
@@ -34,6 +33,7 @@
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
 
 /**
  * Writes a configuration file in
@@ -41,7 +41,7 @@
  * format that gets zipped alongside the APKs to be tested.
  * This config gets ingested by Tradefed.
  */
-@CacheableTask
+@DisableCachingByDefault(because = "Doesn't benefit from caching")
 abstract class GenerateTestConfigurationTask : DefaultTask() {
 
     @get:InputFiles
@@ -52,10 +52,6 @@
     @get:Internal
     abstract val appLoader: Property<BuiltArtifactsLoader>
 
-    @get:Input
-    @get:Optional
-    abstract val appProjectPath: Property<String>
-
     @get:InputFiles
     @get:PathSensitive(PathSensitivity.RELATIVE)
     abstract val testFolder: DirectoryProperty
@@ -96,6 +92,18 @@
     @get:OutputFile
     abstract val constrainedOutputXml: RegularFileProperty
 
+    @get:OutputFile
+    abstract val outputTestApk: RegularFileProperty
+
+    @get:OutputFile
+    abstract val constrainedOutputTestApk: RegularFileProperty
+
+    @get:[OutputFile Optional]
+    abstract val outputAppApk: RegularFileProperty
+
+    @get:[OutputFile Optional]
+    abstract val constrainedOutputAppApk: RegularFileProperty
+
     @TaskAction
     fun generateAndroidTestZip() {
         writeConfigFileContent(
@@ -125,9 +133,13 @@
                 ?: throw RuntimeException("Cannot load required APK for task: $name")
             // We don't need to check hasBenchmarkPlugin because benchmarks shouldn't have test apps
             val appApkBuiltArtifact = appApk.elements.single()
-            val appName = appApkBuiltArtifact.outputFile.substringAfterLast("/")
-                .renameApkForTesting(appProjectPath.get())
-            configBuilder.appApkName(appName)
+            val destinationApk = if (isConstrained) {
+                constrainedOutputAppApk.get().asFile
+            } else {
+                outputAppApk.get().asFile
+            }
+            File(appApkBuiltArtifact.outputFile).copyTo(destinationApk, overwrite = true)
+            configBuilder.appApkName(destinationApk.name)
                 .appApkSha256(sha256(File(appApkBuiltArtifact.outputFile)))
         }
         configBuilder.additionalApkKeys(additionalApkKeys.get())
@@ -181,16 +193,25 @@
         val testApk = testLoader.get().load(testFolder.get())
             ?: throw RuntimeException("Cannot load required APK for task: $name")
         val testApkBuiltArtifact = testApk.elements.single()
-        val testName = testApkBuiltArtifact.outputFile
-            .substringAfterLast("/")
-            .renameApkForTesting(testProjectPath.get())
-        configBuilder.testApkName(testName)
+        val destinationApk = if (isConstrained) {
+            constrainedOutputTestApk.get().asFile
+        } else {
+            outputTestApk.get().asFile
+        }
+        File(testApkBuiltArtifact.outputFile).copyTo(destinationApk, overwrite = true)
+        configBuilder.testApkName(destinationApk.name)
             .applicationId(testApk.applicationId)
             .minSdk(minSdk.get().toString())
             .testRunner(testRunner.get())
             .testApkSha256(sha256(File(testApkBuiltArtifact.outputFile)))
         createOrFail(outputFile).writeText(configBuilder.buildXml())
         if (!isConstrained) {
+            if (!outputJson.asFile.get().name.startsWith("_")) {
+                // Prefixing json file names with _ allows us to collocate these files
+                // inside of the androidTest.zip to make fetching them less expensive.
+                throw GradleException("json output file names are expected to use _ prefix to, " +
+                    "currently set to ${outputJson.asFile.get().name}")
+            }
             createOrFail(outputJson).writeText(configBuilder.buildJson())
         }
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index 1c9a5e5..8ca7eb7 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -30,20 +30,16 @@
 import androidx.build.hasAndroidTestSourceCode
 import androidx.build.hasBenchmarkPlugin
 import androidx.build.isPresubmitBuild
-import androidx.build.renameApkForTesting
 import com.android.build.api.artifact.Artifacts
 import com.android.build.api.artifact.SingleArtifact
 import com.android.build.api.variant.AndroidComponentsExtension
 import com.android.build.api.variant.ApplicationAndroidComponentsExtension
 import com.android.build.api.variant.HasAndroidTest
 import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.tasks.PackageAndroidArtifact
 import java.io.File
 import org.gradle.api.Project
 import org.gradle.api.UnknownTaskException
-import org.gradle.api.file.DuplicatesStrategy
 import org.gradle.api.tasks.TaskProvider
-import org.gradle.api.tasks.bundling.Zip
 import org.gradle.kotlin.dsl.getByType
 
 /**
@@ -62,7 +58,7 @@
     overrideProject: Project = this
 ) {
     val xmlName = "${path.asFilenamePrefix()}$variantName.xml"
-    val jsonName = "${path.asFilenamePrefix()}$variantName.json"
+    val jsonName = "_${path.asFilenamePrefix()}$variantName.json"
     rootProject.tasks.named("createModuleInfo").configure {
         it as ModuleInfoGenerator
         it.testModules.add(
@@ -80,6 +76,15 @@
 
         task.testFolder.set(artifacts.get(SingleArtifact.APK))
         task.testLoader.set(artifacts.getBuiltArtifactsLoader())
+        task.outputTestApk.set(
+            File(getTestConfigDirectory(), "${path.asFilenamePrefix()}-$variantName.apk")
+        )
+        task.constrainedOutputTestApk.set(
+            File(
+                getConstrainedTestConfigDirectory(),
+                "${path.asFilenamePrefix()}-$variantName.apk"
+            )
+        )
         task.additionalApkKeys.set(androidXExtension.additionalDeviceTestApkKeys)
         task.additionalTags.set(androidXExtension.additionalDeviceTestTags)
         task.outputXml.fileValue(File(getTestConfigDirectory(), xmlName))
@@ -130,20 +135,42 @@
                 getOrCreateMacrobenchmarkConfigTask().configure { configTask ->
                     configTask.appFolder.set(appVariant.artifacts.get(SingleArtifact.APK))
                     configTask.appLoader.set(appVariant.artifacts.getBuiltArtifactsLoader())
-                    configTask.appProjectPath.set(path)
+                    configTask.outputAppApk.set(
+                        File(
+                            getTestConfigDirectory(),
+                            "${path.asFilenamePrefix()}-${appVariant.name}.apk"
+                        )
+                    )
+                    configTask.constrainedOutputAppApk.set(
+                        File(
+                            getConstrainedTestConfigDirectory(),
+                            "${path.asFilenamePrefix()}-${appVariant.name}.apk"
+                        )
+                    )
                 }
                 if (path == ":benchmark:integration-tests:macrobenchmark-target") {
                     // Ugly workaround for b/188699825 where we hardcode that
                     // :benchmark:integration-tests:macrobenchmark-target needs to be installed
                     // for :benchmark:benchmark-macro tests to work.
-                    project(":benchmark:benchmark-macro").tasks.withType(
+                    project(MACRO_PROJECT).tasks.withType(
                         GenerateTestConfigurationTask::class.java
                     ).named(
                         "${AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK}debugAndroidTest"
                     ).configure { configTask ->
                         configTask.appFolder.set(appVariant.artifacts.get(SingleArtifact.APK))
                         configTask.appLoader.set(appVariant.artifacts.getBuiltArtifactsLoader())
-                        configTask.appProjectPath.set(path)
+                        configTask.outputAppApk.set(
+                            File(
+                                getTestConfigDirectory(),
+                                "${MACRO_PROJECT.asFilenamePrefix()}-${appVariant.name}.apk"
+                            )
+                        )
+                        configTask.constrainedOutputAppApk.set(
+                            File(
+                                getConstrainedTestConfigDirectory(),
+                                "${MACRO_PROJECT.asFilenamePrefix()}-${appVariant.name}.apk"
+                            )
+                        )
                     }
                 }
             }
@@ -160,42 +187,23 @@
                 configTask as GenerateTestConfigurationTask
                 configTask.appFolder.set(appVariant.artifacts.get(SingleArtifact.APK))
                 configTask.appLoader.set(appVariant.artifacts.getBuiltArtifactsLoader())
-                configTask.appProjectPath.set(path)
+                configTask.outputAppApk.set(
+                    File(
+                        getTestConfigDirectory(),
+                        "${path.asFilenamePrefix()}-${appVariant.name}.apk"
+                    )
+                )
+                configTask.constrainedOutputAppApk.set(
+                    File(
+                        getConstrainedTestConfigDirectory(),
+                        "${path.asFilenamePrefix()}-${appVariant.name}.apk"
+                    )
+                )
             }
         }
     }
 }
 
-/**
- * Configures the test zip task to include the project's apk
- */
-fun addToTestZips(project: Project, packageTask: PackageAndroidArtifact) {
-    project.rootProject.tasks.named(ZIP_TEST_CONFIGS_WITH_APKS_TASK) { task ->
-        task as Zip
-        val projectPath = project.path
-        task.from(packageTask.outputDirectory) {
-            it.include("*.apk")
-            it.duplicatesStrategy = DuplicatesStrategy.FAIL
-            it.rename { fileName ->
-                fileName.renameApkForTesting(projectPath)
-            }
-        }
-        task.dependsOn(packageTask)
-    }
-    project.rootProject.tasks.named(ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK) { task ->
-        task as Zip
-        val projectPath = project.path
-        task.from(packageTask.outputDirectory) {
-            it.include("*.apk")
-            it.duplicatesStrategy = DuplicatesStrategy.FAIL
-            it.rename { fileName ->
-                fileName.renameApkForTesting(projectPath)
-            }
-        }
-        task.dependsOn(packageTask)
-    }
-}
-
 private fun getOrCreateMediaTestConfigTask(project: Project, isMedia2: Boolean):
     TaskProvider<GenerateMediaTestConfigurationTask> {
         val mediaPrefix = getMediaConfigTaskPrefix(isMedia2)
@@ -253,59 +261,67 @@
             if (this.name.contains("previous")) {
                 it.clientPreviousFolder.set(artifacts.get(SingleArtifact.APK))
                 it.clientPreviousLoader.set(artifacts.getBuiltArtifactsLoader())
-                it.clientPreviousPath.set(this.path)
             } else {
                 it.clientToTFolder.set(artifacts.get(SingleArtifact.APK))
                 it.clientToTLoader.set(artifacts.getBuiltArtifactsLoader())
-                it.clientToTPath.set(this.path)
             }
         } else {
             if (this.name.contains("previous")) {
                 it.servicePreviousFolder.set(artifacts.get(SingleArtifact.APK))
                 it.servicePreviousLoader.set(artifacts.getBuiltArtifactsLoader())
-                it.servicePreviousPath.set(this.path)
             } else {
                 it.serviceToTFolder.set(artifacts.get(SingleArtifact.APK))
                 it.serviceToTLoader.set(artifacts.getBuiltArtifactsLoader())
-                it.serviceToTPath.set(this.path)
             }
         }
         it.jsonClientPreviousServiceToTClientTests.fileValue(
             File(
                 this.getTestConfigDirectory(),
-                "${mediaPrefix}ClientPreviousServiceToTClientTests$variantName.json"
+                "_${mediaPrefix}ClientPreviousServiceToTClientTests$variantName.json"
             )
         )
         it.jsonClientPreviousServiceToTServiceTests.fileValue(
             File(
                 this.getTestConfigDirectory(),
-                "${mediaPrefix}ClientPreviousServiceToTServiceTests$variantName.json"
+                "_${mediaPrefix}ClientPreviousServiceToTServiceTests$variantName.json"
             )
         )
         it.jsonClientToTServicePreviousClientTests.fileValue(
             File(
                 this.getTestConfigDirectory(),
-                "${mediaPrefix}ClientToTServicePreviousClientTests$variantName.json"
+                "_${mediaPrefix}ClientToTServicePreviousClientTests$variantName.json"
             )
         )
         it.jsonClientToTServicePreviousServiceTests.fileValue(
             File(
                 this.getTestConfigDirectory(),
-                "${mediaPrefix}ClientToTServicePreviousServiceTests$variantName.json"
+                "_${mediaPrefix}ClientToTServicePreviousServiceTests$variantName.json"
             )
         )
         it.jsonClientToTServiceToTClientTests.fileValue(
             File(
                 this.getTestConfigDirectory(),
-                "${mediaPrefix}ClientToTServiceToTClientTests$variantName.json"
+                "_${mediaPrefix}ClientToTServiceToTClientTests$variantName.json"
             )
         )
         it.jsonClientToTServiceToTServiceTests.fileValue(
             File(
                 this.getTestConfigDirectory(),
-                "${mediaPrefix}ClientToTServiceToTServiceTests$variantName.json"
+                "_${mediaPrefix}ClientToTServiceToTServiceTests$variantName.json"
             )
         )
+        it.totClientApk.fileValue(
+            File(getTestConfigDirectory(), "${mediaPrefix}ClientToT$variantName.apk")
+        )
+        it.previousClientApk.fileValue(
+            File(getTestConfigDirectory(), "${mediaPrefix}ClientPrevious$variantName.apk")
+        )
+        it.totServiceApk.fileValue(
+            File(getTestConfigDirectory(), "${mediaPrefix}ServiceToT$variantName.apk")
+        )
+        it.previousServiceApk.fileValue(
+            File(getTestConfigDirectory(), "${mediaPrefix}ServicePrevious$variantName.apk")
+        )
         it.minSdk.set(minSdk)
         it.testRunner.set(testRunner)
         it.presubmit.set(isPresubmitBuild())
@@ -339,13 +355,22 @@
         val fileNamePrefix = path.asFilenamePrefix()
         task.testFolder.set(artifacts.get(SingleArtifact.APK))
         task.testLoader.set(artifacts.getBuiltArtifactsLoader())
+        task.outputTestApk.set(
+            File(getTestConfigDirectory(), "${path.asFilenamePrefix()}-$variantName.apk")
+        )
+        task.constrainedOutputTestApk.set(
+            File(
+                getConstrainedTestConfigDirectory(),
+                "${path.asFilenamePrefix()}-$variantName.apk"
+            )
+        )
         task.additionalApkKeys.set(androidXExtension.additionalDeviceTestApkKeys)
         task.additionalTags.set(androidXExtension.additionalDeviceTestTags)
         task.outputXml.fileValue(
             File(getTestConfigDirectory(), "$fileNamePrefix$variantName.xml")
         )
         task.outputJson.fileValue(
-            File(getTestConfigDirectory(), "$fileNamePrefix$variantName.json")
+            File(getTestConfigDirectory(), "_$fileNamePrefix$variantName.json")
         )
         task.constrainedOutputXml.fileValue(
             File(
@@ -448,3 +473,5 @@
         }
     }
 }
+
+private const val MACRO_PROJECT = ":benchmark:benchmark-macro"
\ No newline at end of file
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
index de3d495..e0087b3 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -25,7 +25,7 @@
     const val DEFAULT_MIN_SDK_VERSION = 14
     const val INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
     private const val INTERNAL_BUILD_TOOLS_VERSION = "34.0.0-rc3"
-    private const val PUBLIC_BUILD_TOOLS_VERSION = "33.0.1"
+    private const val PUBLIC_BUILD_TOOLS_VERSION = "34.0.0-rc3"
     const val NDK_VERSION = "23.1.7779620"
 
     /**
diff --git a/buildSrc/repos.gradle b/buildSrc/repos.gradle
index 9b5077d..4ae93fa 100644
--- a/buildSrc/repos.gradle
+++ b/buildSrc/repos.gradle
@@ -65,6 +65,13 @@
                url("https://maven.pkg.jetbrains.space/public/p/compose/dev")
         }
         handler.mavenLocal()
+        // TODO(b/280646217): Remove after official release to gmaven.
+        handler.maven {
+            url("https://storage.googleapis.com/r8-releases/raw")
+            content {
+                includeModule("com.android.tools", "r8")
+            }
+        }
     }
     // Ordering appears to be important: b/229733266
     def androidPluginRepoOverride = System.getenv("GRADLE_PLUGIN_REPO")
diff --git a/camera/camera-camera2-pipe-integration/src/main/AndroidManifest.xml b/camera/camera-camera2-pipe-integration/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/camera/camera-camera2-pipe-integration/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
index 2b1d488..f920bed 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
@@ -19,6 +19,7 @@
 package androidx.camera.camera2.pipe.integration.adapter
 
 import android.content.Context
+import android.util.Pair
 import android.util.Size
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraId
@@ -132,7 +133,7 @@
         cameraId: String,
         existingSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
-    ): Map<UseCaseConfig<*>, StreamSpec> {
+    ): Pair<Map<UseCaseConfig<*>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>> {
 
         if (!checkIfSupportedCombinationExist(cameraId)) {
             throw IllegalArgumentException(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
index b98730d..516f8a5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
@@ -29,6 +29,7 @@
 import android.media.MediaRecorder
 import android.os.Build
 import android.util.Rational
+import android.util.Pair
 import android.util.Size
 import android.view.Display
 import androidx.annotation.RequiresApi
@@ -197,7 +198,7 @@
         cameraMode: Int,
         existingSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
-    ): Map<UseCaseConfig<*>, StreamSpec> {
+    ): Pair<Map<UseCaseConfig<*>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>> {
         refreshPreviewSize()
         val surfaceConfigs: MutableList<SurfaceConfig> = ArrayList()
         for (scc in existingSurfaces) {
@@ -293,7 +294,7 @@
                     " New configs: " + newUseCaseConfigs
             )
         }
-        return suggestedStreamSpecMap
+        return Pair.create(suggestedStreamSpecMap, mapOf<AttachedSurfaceInfo, StreamSpec>())
     }
 
     /**
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index e0a02ad..a289f28 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -31,9 +31,11 @@
 import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
 import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
 import androidx.camera.core.UseCase
+import androidx.camera.core.impl.CameraInternal
 import androidx.camera.core.impl.DeferrableSurface
 import androidx.camera.core.impl.SessionConfig.ValidatingBuilder
 import javax.inject.Inject
+import javax.inject.Provider
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.joinAll
 
@@ -75,6 +77,7 @@
     private val cameraStateAdapter: CameraStateAdapter,
     private val cameraQuirks: CameraQuirks,
     private val cameraGraphFlags: CameraGraph.Flags,
+    private val cameraInternal: Provider<CameraInternal>,
     cameraProperties: CameraProperties,
     displayInfoManager: DisplayInfoManager,
 ) {
@@ -318,6 +321,7 @@
 
     @GuardedBy("lock")
     private fun addRepeatingUseCase() {
+        meteringRepeating.bindToCamera(cameraInternal.get(), null, null)
         meteringRepeating.setupSession()
         attach(listOf(meteringRepeating))
         activate(meteringRepeating)
@@ -338,7 +342,7 @@
     private fun removeRepeatingUseCase() {
         deactivate(meteringRepeating)
         detach(listOf(meteringRepeating))
-        meteringRepeating.onUnbind()
+        meteringRepeating.unbindFromCamera(cameraInternal.get())
     }
 
     private fun Collection<UseCase>.onlyVideoCapture(): Boolean {
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
index af3b4d6..c758dc4 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
@@ -1572,7 +1572,7 @@
             CameraMode.DEFAULT,
             attachedSurfaceInfoList,
             useCaseConfigToOutputSizesMap
-        )
+        ).first
 
         useCasesExpectedResultMap.keys.forEach {
             val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
index 9248cf6..d4488e2 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -312,25 +312,29 @@
         controls: Set<UseCaseCameraControl> = emptySet(),
         useCaseCameraComponentBuilder: FakeUseCaseCameraComponentBuilder =
             FakeUseCaseCameraComponentBuilder(),
-    ) = UseCaseManager(
-        cameraConfig = CameraConfig(CameraId("0")),
-        builder = useCaseCameraComponentBuilder,
-        controls = controls as java.util.Set<UseCaseCameraControl>,
-        cameraProperties = FakeCameraProperties(),
-        camera2CameraControl = Camera2CameraControl.create(
-            FakeCamera2CameraControlCompat(),
-            useCaseThreads,
-            ComboRequestListener()
-        ),
-        cameraStateAdapter = CameraStateAdapter(),
-        cameraGraphFlags = CameraGraph.Flags(),
-        cameraQuirks = CameraQuirks(
-            FakeCameraMetadata(),
-            StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
-        ),
-        displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext()),
-    ).also {
-        useCaseManagerList.add(it)
+    ): UseCaseManager {
+        val fakeCamera = FakeCamera()
+        return UseCaseManager(
+            cameraConfig = CameraConfig(CameraId("0")),
+            builder = useCaseCameraComponentBuilder,
+            controls = controls as java.util.Set<UseCaseCameraControl>,
+            cameraProperties = FakeCameraProperties(),
+            camera2CameraControl = Camera2CameraControl.create(
+                FakeCamera2CameraControlCompat(),
+                useCaseThreads,
+                ComboRequestListener()
+            ),
+            cameraStateAdapter = CameraStateAdapter(),
+            cameraGraphFlags = CameraGraph.Flags(),
+            cameraInternal = { fakeCamera },
+            cameraQuirks = CameraQuirks(
+                FakeCameraMetadata(),
+                StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
+            ),
+            displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext()),
+        ).also {
+            useCaseManagerList.add(it)
+        }
     }
 
     private fun createImageCapture(): ImageCapture =
diff --git a/camera/camera-camera2-pipe-testing/src/main/AndroidManifest.xml b/camera/camera-camera2-pipe-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index 9515a11..0000000
--- a/camera/camera-camera2-pipe-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/camera/camera-camera2-pipe/src/main/AndroidManifest.xml b/camera/camera-camera2-pipe/src/main/AndroidManifest.xml
deleted file mode 100644
index 9515a11..0000000
--- a/camera/camera-camera2-pipe/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
index 053529d..cfded9e 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
@@ -174,6 +174,10 @@
         val callback = FakeImageCaptureCallback()
         imageCapture.takePicture(CameraXExecutors.directExecutor(), callback)
         callback.awaitCapturesAndAssert()
+
+        withContext(Dispatchers.Main) {
+            cameraUseCaseAdapter.removeUseCases(cameraUseCaseAdapter.useCases)
+        }
     }
 
     @SdkSuppress(minSdkVersion = 28)
@@ -229,6 +233,10 @@
         val callback = FakeImageCaptureCallback()
         imageCapture.takePicture(CameraXExecutors.directExecutor(), callback)
         callback.awaitCapturesAndAssert()
+
+        withContext(Dispatchers.Main) {
+            cameraUseCaseAdapter.removeUseCases(cameraUseCaseAdapter.useCases)
+        }
     }
 
     private fun enableSessionProcessor(
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
index a85be9d..7c1d95e 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.camera2.CameraDevice;
 import android.media.CamcorderProfile;
+import android.util.Pair;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
@@ -60,7 +61,6 @@
 
     /**
      * Creates a new, initialized Camera2DeviceSurfaceManager.
-     *
      */
     @RestrictTo(Scope.LIBRARY)
     public Camera2DeviceSurfaceManager(@NonNull Context context,
@@ -166,8 +166,10 @@
      */
     @NonNull
     @Override
-    public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            @CameraMode.Mode int cameraMode, @NonNull String cameraId,
+    public Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
+            getSuggestedStreamSpecs(
+            @CameraMode.Mode int cameraMode,
+            @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
         Preconditions.checkArgument(!newUseCaseConfigsSupportedSizeMap.isEmpty(),
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
index 5481b76..1c8859f 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
@@ -26,12 +26,10 @@
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
 
 import android.content.Context;
-import android.hardware.camera2.CameraDevice;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
-import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCapture.CaptureMode;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
@@ -66,22 +64,8 @@
         final MutableOptionsBundle mutableConfig = MutableOptionsBundle.create();
 
         SessionConfig.Builder sessionBuilder = new SessionConfig.Builder();
-        switch (captureType) {
-            case IMAGE_CAPTURE:
-                sessionBuilder.setTemplateType(
-                        captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG
-                                ? CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG :
-                                CameraDevice.TEMPLATE_PREVIEW);
-                break;
-            case PREVIEW:
-            case IMAGE_ANALYSIS:
-                sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
-                break;
-            case VIDEO_CAPTURE:
-            case STREAM_SHARING:
-                sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_RECORD);
-                break;
-        }
+        sessionBuilder.setTemplateType(
+                TemplateTypeUtil.getSessionConfigTemplateType(captureType, captureMode));
 
         mutableConfig.insertOption(OPTION_DEFAULT_SESSION_CONFIG, sessionBuilder.build());
 
@@ -89,23 +73,8 @@
                 Camera2SessionOptionUnpacker.INSTANCE);
 
         CaptureConfig.Builder captureBuilder = new CaptureConfig.Builder();
-
-        switch (captureType) {
-            case IMAGE_CAPTURE:
-                captureBuilder.setTemplateType(
-                        captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG
-                                ? CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG :
-                                CameraDevice.TEMPLATE_STILL_CAPTURE);
-                break;
-            case PREVIEW:
-            case IMAGE_ANALYSIS:
-                captureBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
-                break;
-            case VIDEO_CAPTURE:
-            case STREAM_SHARING:
-                captureBuilder.setTemplateType(CameraDevice.TEMPLATE_RECORD);
-                break;
-        }
+        captureBuilder.setTemplateType(
+                TemplateTypeUtil.getCaptureConfigTemplateType(captureType, captureMode));
         mutableConfig.insertOption(OPTION_DEFAULT_CAPTURE_CONFIG, captureBuilder.build());
 
         // Only CAPTURE_TYPE_IMAGE_CAPTURE has its own ImageCaptureOptionUnpacker. Other
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
index 5d8196d..1f1efeb 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
@@ -16,8 +16,6 @@
 
 package androidx.camera.camera2.internal;
 
-import static androidx.camera.camera2.impl.Camera2ImplConfig.STREAM_USE_CASE_OPTION;
-
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraMetadata;
@@ -28,27 +26,44 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
+import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.ImageAnalysis;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.Preview;
 import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.AttachedSurfaceInfo;
+import androidx.camera.core.impl.CameraMode;
+import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.DeferrableSurface;
+import androidx.camera.core.impl.ImageCaptureConfig;
+import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.StreamSpec;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
 import androidx.camera.core.streamsharing.StreamSharing;
+import androidx.core.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 /**
  * A class that contains utility methods for stream use case.
  */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public final class StreamUseCaseUtil {
 
+    public static final Config.Option<Long> STREAM_USE_CASE_STREAM_SPEC_OPTION =
+            Config.Option.create("camera2.streamSpec.streamUseCase", long.class);
+
     private StreamUseCaseUtil() {
 
     }
@@ -93,11 +108,12 @@
             }
             for (DeferrableSurface surface : sessionConfig.getSurfaces()) {
                 if (sessionConfig.getImplementationOptions().containsOption(
-                        STREAM_USE_CASE_OPTION) && putStreamUseCaseToMappingIfAvailable(
+                        Camera2ImplConfig.STREAM_USE_CASE_OPTION)
+                        && putStreamUseCaseToMappingIfAvailable(
                         streamUseCaseMap,
                         surface,
                         sessionConfig.getImplementationOptions().retrieveOption(
-                                STREAM_USE_CASE_OPTION),
+                                Camera2ImplConfig.STREAM_USE_CASE_OPTION),
                         supportedStreamUseCases)) {
                     continue;
                 }
@@ -155,4 +171,272 @@
         }
         return sUseCaseToStreamUseCaseMapping;
     }
+
+    /**
+     * Populate all implementation options needed to determine the StreamUseCase option in the
+     * StreamSpec for this UseCaseConfig
+     */
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
+    @NonNull
+    public static Camera2ImplConfig getStreamSpecImplementationOptions(
+            @NonNull UseCaseConfig<?> useCaseConfig
+    ) {
+        MutableOptionsBundle optionsBundle = MutableOptionsBundle.create();
+        if (useCaseConfig.containsOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)) {
+            optionsBundle.insertOption(
+                    Camera2ImplConfig.STREAM_USE_CASE_OPTION,
+                    useCaseConfig.retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)
+            );
+        }
+        if (useCaseConfig.containsOption(UseCaseConfig.OPTION_ZSL_DISABLED)) {
+            optionsBundle.insertOption(
+                    UseCaseConfig.OPTION_ZSL_DISABLED,
+                    useCaseConfig.retrieveOption(UseCaseConfig.OPTION_ZSL_DISABLED)
+            );
+        }
+        if (useCaseConfig.containsOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)) {
+            optionsBundle.insertOption(
+                    ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE,
+                    useCaseConfig
+                            .retrieveOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)
+            );
+        }
+        if (useCaseConfig.containsOption(UseCaseConfig.OPTION_INPUT_FORMAT)) {
+            optionsBundle.insertOption(
+                    UseCaseConfig.OPTION_INPUT_FORMAT,
+                    useCaseConfig
+                            .retrieveOption(UseCaseConfig.OPTION_INPUT_FORMAT)
+            );
+        }
+        return new Camera2ImplConfig(optionsBundle);
+    }
+
+    /**
+     * Populate the {@link STREAM_USE_CASE_STREAM_SPEC_OPTION} option in StreamSpecs for both
+     * existing UseCases and new UseCases to be attached. This option will be written into the
+     * session configurations of the UseCases. When creating a new capture session during
+     * downstream, it will be used to set the StreamUseCase flag via
+     * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase(long)}
+     *
+     * @param characteristicsCompat        the camera characteristics of the device
+     * @param featureSettings              the feature settings of this capture
+     * @param attachedSurfaces             surface info of the already attached use cases
+     * @param suggestedStreamSpecMap       the UseCaseConfig-to-StreamSpec map for new use cases
+     * @param attachedSurfaceStreamSpecMap the SurfaceInfo-to-StreamSpec map for attached use cases
+     *                                     whose StreamSpecs needs to be updated
+     */
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
+    public static void populateStreamUseCaseStreamSpecOption(
+            @NonNull CameraCharacteristicsCompat characteristicsCompat,
+            @NonNull SupportedSurfaceCombination.FeatureSettings featureSettings,
+            @NonNull List<AttachedSurfaceInfo> attachedSurfaces,
+            @NonNull Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap,
+            @NonNull Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap) {
+        if (Build.VERSION.SDK_INT < 33) {
+            return;
+        }
+        long[] availableStreamUseCases = characteristicsCompat.get(
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
+        if (availableStreamUseCases == null || availableStreamUseCases.length == 0) {
+            return;
+        }
+        if (featureSettings.getCameraMode() != CameraMode.DEFAULT
+                || featureSettings.getRequiredMaxBitDepth() != DynamicRange.BIT_DEPTH_8_BIT) {
+            return;
+        }
+        List<UseCaseConfig<?>> newUseCaseConfigs = new ArrayList<>(suggestedStreamSpecMap.keySet());
+        // All AttachedSurfaceInfo should have implementation options
+        for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
+            Preconditions.checkNotNull(attachedSurfaceInfo.getImplementationOptions());
+        }
+        // All StreamSpecs in the map should have implementation options
+        for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
+            Preconditions.checkNotNull(Preconditions.checkNotNull(
+                    suggestedStreamSpecMap.get(useCaseConfig)).getImplementationOptions());
+        }
+        if (containsZslUseCase(attachedSurfaces, newUseCaseConfigs)) {
+            return;
+        }
+        Set<Long> availableStreamUseCaseSet = new HashSet<>();
+        for (Long availableStreamUseCase : availableStreamUseCases) {
+            availableStreamUseCaseSet.add(availableStreamUseCase);
+        }
+        if (isValidCamera2InteropOverride(attachedSurfaces, newUseCaseConfigs,
+                availableStreamUseCaseSet)) {
+            for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
+                Config oldImplementationOptions = attachedSurfaceInfo.getImplementationOptions();
+                Config newImplementationOptions =
+                        getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
+                                oldImplementationOptions,
+                                oldImplementationOptions.retrieveOption(
+                                        Camera2ImplConfig.STREAM_USE_CASE_OPTION));
+                if (newImplementationOptions != null) {
+                    StreamSpec.Builder newStreamSpecBuilder =
+                            StreamSpec.builder(attachedSurfaceInfo.getSize())
+                                    .setDynamicRange(attachedSurfaceInfo.getDynamicRange())
+                                    .setImplementationOptions(newImplementationOptions);
+                    if (attachedSurfaceInfo.getTargetFrameRate() != null) {
+                        newStreamSpecBuilder.setExpectedFrameRateRange(
+                                attachedSurfaceInfo.getTargetFrameRate());
+                    }
+                    attachedSurfaceStreamSpecMap.put(attachedSurfaceInfo,
+                            newStreamSpecBuilder.build());
+                }
+            }
+            for (UseCaseConfig<?> newUseCaseConfig : newUseCaseConfigs) {
+                StreamSpec oldStreamSpec = suggestedStreamSpecMap.get(newUseCaseConfig);
+                Config oldImplementationOptions = oldStreamSpec.getImplementationOptions();
+                Config newImplementationOptions =
+                        getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
+                                oldImplementationOptions, oldImplementationOptions.retrieveOption(
+                                        Camera2ImplConfig.STREAM_USE_CASE_OPTION));
+                if (newImplementationOptions != null) {
+                    StreamSpec newStreamSpec =
+                            oldStreamSpec.toBuilder().setImplementationOptions(
+                                    newImplementationOptions).build();
+                    suggestedStreamSpecMap.put(newUseCaseConfig, newStreamSpec);
+                }
+            }
+        }
+    }
+
+    /**
+     * Given an old options, return a new option with stream use case stream spec option inserted
+     */
+    @Nullable
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
+    private static Config getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
+            Config oldImplementationOptions,
+            long streamUseCase
+    ) {
+        if (oldImplementationOptions.containsOption(STREAM_USE_CASE_STREAM_SPEC_OPTION)
+                && oldImplementationOptions.retrieveOption(STREAM_USE_CASE_STREAM_SPEC_OPTION)
+                == streamUseCase) {
+            // The old options already has the same stream use case. No need to update
+            return null;
+        }
+        MutableOptionsBundle optionsBundle =
+                MutableOptionsBundle.from(oldImplementationOptions);
+        optionsBundle.insertOption(STREAM_USE_CASE_STREAM_SPEC_OPTION, streamUseCase);
+        return new Camera2ImplConfig(optionsBundle);
+    }
+
+    /**
+     * Return true if any one of the existing or new UseCases is ZSL.
+     */
+    private static boolean containsZslUseCase(
+            List<AttachedSurfaceInfo> attachedSurfaces,
+            List<UseCaseConfig<?>> newUseCaseConfigs) {
+        for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
+            List<UseCaseConfigFactory.CaptureType> captureTypes =
+                    attachedSurfaceInfo.getCaptureTypes();
+            UseCaseConfigFactory.CaptureType captureType = captureTypes.get(0);
+            if (isZslUseCase(
+                    attachedSurfaceInfo.getImplementationOptions(),
+                    captureType)) {
+                return true;
+            }
+        }
+        for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
+            if (isZslUseCase(useCaseConfig, useCaseConfig.getCaptureType())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check whether a UseCase is ZSL.
+     */
+    private static boolean isZslUseCase(Config config,
+            UseCaseConfigFactory.CaptureType captureType) {
+        if (config.retrieveOption(UseCaseConfig.OPTION_ZSL_DISABLED, false)) {
+            return false;
+        }
+        // Skip if capture mode doesn't exist in the options
+        if (!config.containsOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)) {
+            return false;
+        }
+
+        @ImageCapture.CaptureMode int captureMode =
+                config.retrieveOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE);
+        return TemplateTypeUtil.getSessionConfigTemplateType(captureType, captureMode)
+                == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG;
+    }
+
+    /**
+     * Check whether the given StreamUseCases are available to the device.
+     */
+    private static boolean areStreamUseCasesAvailable(Set<Long> availableStreamUseCasesSet,
+            Set<Long> streamUseCases) {
+        for (Long streamUseCase : streamUseCases) {
+            if (!availableStreamUseCasesSet.contains(streamUseCase)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static void throwInvalidCamera2InteropOverrideException() {
+        throw new IllegalArgumentException("Either all use cases must have non-default stream use "
+                + "case assigned or none should have it");
+    }
+
+    /**
+     * Return true if all existing UseCases and new UseCases have Camera2Interop override and
+     * these StreamUseCases are all available to the device.
+     */
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
+    private static boolean isValidCamera2InteropOverride(
+            List<AttachedSurfaceInfo> attachedSurfaces,
+            List<UseCaseConfig<?>> newUseCaseConfigs,
+            Set<Long> availableStreamUseCases) {
+        Set<Long> streamUseCases = new HashSet<>();
+        boolean hasNonDefaultStreamUseCase = false;
+        boolean hasDefaultOrNullStreamUseCase = false;
+        for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
+            if (!attachedSurfaceInfo.getImplementationOptions().containsOption(
+                    Camera2ImplConfig.STREAM_USE_CASE_OPTION)) {
+                hasDefaultOrNullStreamUseCase = true;
+                break;
+            }
+            long streamUseCaseOverride =
+                    attachedSurfaceInfo.getImplementationOptions()
+                            .retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION);
+            if (streamUseCaseOverride
+                    == CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+                hasDefaultOrNullStreamUseCase = true;
+                break;
+            }
+            hasNonDefaultStreamUseCase = true;
+            break;
+        }
+        for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
+            if (!useCaseConfig.containsOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)) {
+                hasDefaultOrNullStreamUseCase = true;
+                if (hasNonDefaultStreamUseCase) {
+                    throwInvalidCamera2InteropOverrideException();
+                }
+            } else {
+                long streamUseCaseOverride =
+                        useCaseConfig.retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION);
+                if (streamUseCaseOverride
+                        == CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+                    hasDefaultOrNullStreamUseCase = true;
+                    if (hasNonDefaultStreamUseCase) {
+                        throwInvalidCamera2InteropOverrideException();
+                    }
+                } else {
+                    hasNonDefaultStreamUseCase = true;
+                    if (hasDefaultOrNullStreamUseCase) {
+                        throwInvalidCamera2InteropOverrideException();
+                    }
+                    streamUseCases.add(streamUseCaseOverride);
+                }
+            }
+
+        }
+        return !hasDefaultOrNullStreamUseCase && areStreamUseCasesAvailable(availableStreamUseCases,
+                streamUseCases);
+    }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index 59fee41..b9a91cf 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -33,6 +33,7 @@
 import android.media.CamcorderProfile;
 import android.media.MediaRecorder;
 import android.os.Build;
+import android.util.Pair;
 import android.util.Range;
 import android.util.Rational;
 import android.util.Size;
@@ -40,6 +41,7 @@
 import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
@@ -49,6 +51,7 @@
 import androidx.camera.camera2.internal.compat.workaround.ExtraSupportedSurfaceCombinationsContainer;
 import androidx.camera.camera2.internal.compat.workaround.ResolutionCorrector;
 import androidx.camera.camera2.internal.compat.workaround.TargetAspectRatio;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraUnavailableException;
 import androidx.camera.core.DynamicRange;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
@@ -490,16 +493,20 @@
      * @param attachedSurfaces                  the existing surfaces.
      * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output
      *                                          sizes map.
-     * @return the suggested stream specifications, which is a mapping from UseCaseConfig to the
-     * suggested stream specification.
+     * @return the suggested stream specifications, which is a pair of mappings. The first
+     * mapping is from UseCaseConfig to the suggested stream specification representing new
+     * UseCases. The second mapping is from attachedSurfaceInfo to the suggested stream
+     * specifications representing existing UseCases.
      * @throws IllegalArgumentException if the suggested solution for newUseCaseConfigs cannot be
      *                                  found. This may be due to no available output size, no
      *                                  available surface combination, unsupported combinations
      *                                  of {@link DynamicRange}, or requiring an
      *                                  unsupported combination of camera features.
      */
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     @NonNull
-    Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecifications(
+    Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
+            getSuggestedStreamSpecifications(
             @CameraMode.Mode int cameraMode,
             @NonNull List<AttachedSurfaceInfo> attachedSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
@@ -669,7 +676,10 @@
                         useCasesPriorityOrder.indexOf(newUseCaseConfigs.indexOf(useCaseConfig)));
                 StreamSpec.Builder streamSpecBuilder = StreamSpec.builder(resolutionForUseCase)
                         .setDynamicRange(Preconditions.checkNotNull(
-                                resolvedDynamicRanges.get(useCaseConfig)));
+                                resolvedDynamicRanges.get(useCaseConfig)))
+                        .setImplementationOptions(
+                                StreamUseCaseUtil.getStreamSpecImplementationOptions(useCaseConfig)
+                        );
                 if (targetFramerateForDevice != null) {
                     streamSpecBuilder.setExpectedFrameRateRange(targetFramerateForDevice);
                 }
@@ -683,7 +693,9 @@
                             + " Existing surfaces: " + attachedSurfaces
                             + " New configs: " + newUseCaseConfigs);
         }
-        return suggestedStreamSpecMap;
+        Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+        // TODO(b/266879290): Invoke StreamUSeCaseUtil.populateStreamUseCaseStreamSpecOption()
+        return new Pair<>(suggestedStreamSpecMap, attachedSurfaceStreamSpecMap);
     }
 
     /**
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TemplateTypeUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TemplateTypeUtil.java
new file mode 100644
index 0000000..905e01f
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TemplateTypeUtil.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal;
+
+import android.hardware.camera2.CameraDevice;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ExperimentalZeroShutterLag;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.impl.UseCaseConfigFactory;
+
+/**
+ * A class that contains utility methods for template type.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class TemplateTypeUtil {
+
+    private TemplateTypeUtil() {
+
+    }
+
+    /**
+     * Returns the appropriate template type for a session configuration.
+     */
+    @OptIn(markerClass = ExperimentalZeroShutterLag.class)
+    public static int getSessionConfigTemplateType(
+            @NonNull UseCaseConfigFactory.CaptureType captureType,
+            @ImageCapture.CaptureMode int captureMode
+    ) {
+        switch (captureType) {
+            case IMAGE_CAPTURE:
+                return captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG
+                        ? CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG :
+                        CameraDevice.TEMPLATE_PREVIEW;
+            case VIDEO_CAPTURE:
+            case STREAM_SHARING:
+                return CameraDevice.TEMPLATE_RECORD;
+            case PREVIEW:
+            case IMAGE_ANALYSIS:
+            default:
+                return CameraDevice.TEMPLATE_PREVIEW;
+        }
+    }
+
+    /**
+     * Returns the appropriate template type for a capture configuration.
+     */
+    @OptIn(markerClass = ExperimentalZeroShutterLag.class)
+    public static int getCaptureConfigTemplateType(
+            @NonNull UseCaseConfigFactory.CaptureType captureType,
+            @ImageCapture.CaptureMode int captureMode
+    ) {
+        switch (captureType) {
+            case IMAGE_CAPTURE:
+                return captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG
+                        ? CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG :
+                        CameraDevice.TEMPLATE_STILL_CAPTURE;
+            case VIDEO_CAPTURE:
+            case STREAM_SHARING:
+                return CameraDevice.TEMPLATE_RECORD;
+            case PREVIEW:
+            case IMAGE_ANALYSIS:
+            default:
+                return CameraDevice.TEMPLATE_PREVIEW;
+        }
+    }
+}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
index 3c1f547..3919413 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
@@ -18,8 +18,14 @@
 
 import static android.os.Build.VERSION.SDK_INT;
 
+import static androidx.camera.camera2.internal.StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION;
+import static androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY;
+import static androidx.camera.core.ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG;
+
+import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraMetadata;
@@ -30,14 +36,25 @@
 import androidx.annotation.NonNull;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.ImageAnalysis;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.Preview;
+import androidx.camera.core.impl.AttachedSurfaceInfo;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.DeferrableSurface;
+import androidx.camera.core.impl.ImageCaptureConfig;
+import androidx.camera.core.impl.MutableConfig;
 import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.StreamSpec;
+import androidx.camera.core.impl.SurfaceConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
+import androidx.camera.core.internal.utils.SizeUtil;
 import androidx.camera.core.streamsharing.StreamSharing;
 import androidx.camera.testing.fakes.FakeUseCase;
+import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.concurrent.futures.ResolvableFuture;
 import androidx.test.filters.SdkSuppress;
 
@@ -54,6 +71,7 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @RunWith(RobolectricTestRunner.class)
@@ -62,6 +80,10 @@
 
     private CameraCharacteristics mCameraCharacteristics;
     private static final String CAMERA_ID_0 = "0";
+    private static final Long TEST_STREAM_USE_CASE_OPTION_VALUE = Long.valueOf(
+            CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW);
+    private static final @ImageCapture.CaptureMode int TEST_OPTION_IMAGE_CAPTURE_MODE_VALUE =
+            CAPTURE_MODE_MAXIMIZE_QUALITY;
 
     DeferrableSurface mMockSurface = new DeferrableSurface() {
         private final ListenableFuture<Surface> mSurfaceFuture = ResolvableFuture.create();
@@ -283,9 +305,272 @@
                 == CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW);
     }
 
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void getStreamSpecImplementationOptions() {
+        Camera2ImplConfig result =
+                StreamUseCaseUtil.getStreamSpecImplementationOptions(
+                        getFakeUseCaseConfigWithOptions(true, false, false,
+                                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
+        assertTrue(result.retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)
+                == TEST_STREAM_USE_CASE_OPTION_VALUE);
+        assertFalse(result.retrieveOption(UseCaseConfig.OPTION_ZSL_DISABLED));
+        assertTrue(result.retrieveOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)
+                == TEST_OPTION_IMAGE_CAPTURE_MODE_VALUE);
+        assertTrue(result.retrieveOption(UseCaseConfig.OPTION_INPUT_FORMAT)
+                == ImageFormat.PRIVATE);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_streamUseCaseNotAvailable() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(
+                getCameraCharacteristicsCompat(true),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+                new HashMap<>());
+        assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
+                .getImplementationOptions().containsOption(
+                        STREAM_USE_CASE_STREAM_SPEC_OPTION));
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_cameraModeNotSupported() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(
+                getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.CONCURRENT_CAMERA,
+                        DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+                new HashMap<>());
+        assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
+                .getImplementationOptions().containsOption(
+                        STREAM_USE_CASE_STREAM_SPEC_OPTION));
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_bitDepthNotSupported() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(
+                getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_10_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+                new HashMap<>());
+        assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
+                .getImplementationOptions().containsOption(
+                        STREAM_USE_CASE_STREAM_SPEC_OPTION));
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_isZslUseCase() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, true,
+                UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+                new HashMap<>());
+        assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
+                .getImplementationOptions().containsOption(
+                        STREAM_USE_CASE_STREAM_SPEC_OPTION));
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_isZslUseCase_ZslDisabled() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, true, true,
+                UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+                new HashMap<>());
+        assertTrue(suggestedStreamSpecMap.get(
+                useCaseConfig).getImplementationOptions().retrieveOption(
+                STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_isZslSurface() {
+        List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
+        attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, true,
+                UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG));
+        Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, new HashMap<>(),
+                attachedSurfaceStreamSpecMap
+        );
+        assertFalse(attachedSurfaceStreamSpecMap.containsKey(attachedSurfaces.get(0)));
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_isZslSurface_ZslDisabled() {
+        List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
+        attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, true, true,
+                UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG));
+        Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, new HashMap<>(),
+                attachedSurfaceStreamSpecMap
+        );
+        assertTrue(attachedSurfaceStreamSpecMap.get(
+                attachedSurfaces.get(0)).getImplementationOptions().retrieveOption(
+                STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_camera2InteropOverride_singleNewUseCase() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+                new HashMap<>());
+        assertTrue(suggestedStreamSpecMap.get(
+                useCaseConfig).getImplementationOptions().retrieveOption(
+                STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_camera2InteropOverride_singleSurface() {
+        List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
+        attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
+        Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, new HashMap<>(),
+                attachedSurfaceStreamSpecMap
+        );
+        assertTrue(attachedSurfaceStreamSpecMap.get(
+                attachedSurfaces.get(0)).getImplementationOptions().retrieveOption(
+                STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void populateStreamUseCaseStreamSpecOption_camera2InteropOverride_useCaseAndSurface() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
+        attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
+        Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, suggestedStreamSpecMap,
+                attachedSurfaceStreamSpecMap
+        );
+        assertTrue(suggestedStreamSpecMap.get(
+                useCaseConfig).getImplementationOptions().retrieveOption(
+                STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+        assertTrue(attachedSurfaceStreamSpecMap.get(
+                attachedSurfaces.get(0)).getImplementationOptions().retrieveOption(
+                STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void populateStreamUseCaseStreamSpecOption_camera2InteropOverride_missingOverride() {
+        Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(false, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+        suggestedStreamSpecMap.put(useCaseConfig,
+                getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
+        List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
+        attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, false,
+                UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
+        Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+        StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
+                SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
+                        DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, suggestedStreamSpecMap,
+                attachedSurfaceStreamSpecMap
+        );
+    }
+
+    private UseCaseConfig<?> getFakeUseCaseConfigWithOptions(boolean camera2InteropOverride,
+            boolean isZslDisabled, boolean isZslCaptureMode,
+            UseCaseConfigFactory.CaptureType captureType, int imageFormat) {
+        FakeUseCaseConfig.Builder fakeUseCaseConfigBuilder = new FakeUseCaseConfig.Builder(
+                captureType);
+        MutableConfig fakeConfig = fakeUseCaseConfigBuilder.getMutableConfig();
+        if (camera2InteropOverride) {
+            fakeConfig.insertOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION,
+                    TEST_STREAM_USE_CASE_OPTION_VALUE);
+        }
+        fakeConfig.insertOption(UseCaseConfig.OPTION_ZSL_DISABLED, isZslDisabled);
+        fakeConfig.insertOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE,
+                isZslCaptureMode ? CAPTURE_MODE_ZERO_SHUTTER_LAG
+                        : TEST_OPTION_IMAGE_CAPTURE_MODE_VALUE);
+        fakeConfig.insertOption(ImageCaptureConfig.OPTION_INPUT_FORMAT, imageFormat);
+        return fakeUseCaseConfigBuilder.getUseCaseConfig();
+    }
+
+    private AttachedSurfaceInfo getFakeAttachedSurfaceInfo(boolean camera2InteropOverride,
+            boolean isZslDisabled, boolean isZslCaptureMode,
+            UseCaseConfigFactory.CaptureType captureType, int imageFormat) {
+        UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(camera2InteropOverride,
+                isZslDisabled, isZslCaptureMode, captureType, imageFormat);
+        List<UseCaseConfigFactory.CaptureType> captureTypes = new ArrayList<>();
+        captureTypes.add(useCaseConfig.getCaptureType());
+        return AttachedSurfaceInfo.create(SurfaceConfig.create(
+                        SurfaceConfig.ConfigType.PRIV,
+                        SurfaceConfig.ConfigSize.PREVIEW
+                ),
+                useCaseConfig.getInputFormat(),
+                SizeUtil.RESOLUTION_720P,
+                DynamicRange.SDR,
+                captureTypes,
+                StreamUseCaseUtil.getStreamSpecImplementationOptions(useCaseConfig),
+                /*targetFrameRate=*/null);
+    }
+
+    private StreamSpec getFakeStreamSpecFromFakeUseCaseConfig(UseCaseConfig<?> fakeUseCaseConfig) {
+        return StreamSpec.builder(SizeUtil.RESOLUTION_720P)
+                .setDynamicRange(DynamicRange.UNSPECIFIED)
+                .setImplementationOptions(
+                        StreamUseCaseUtil.getStreamSpecImplementationOptions(fakeUseCaseConfig)
+                ).build();
+    }
+
     private CameraCharacteristicsCompat getCameraCharacteristicsCompat() {
+        return getCameraCharacteristicsCompat(false);
+    }
+
+    private CameraCharacteristicsCompat getCameraCharacteristicsCompat(
+            boolean noAvailableStreamUseCase) {
         ShadowCameraCharacteristics shadowCharacteristics0 = Shadow.extract(mCameraCharacteristics);
-        if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+        if (SDK_INT >= Build.VERSION_CODES.TIRAMISU && !noAvailableStreamUseCase) {
             long[] uc = new long[]{CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
                     CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW,
                     CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL,
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index b11b2a5a..d0ea2aa 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -1558,7 +1558,7 @@
             cameraMode,
             attachedSurfaceInfoList,
             useCaseConfigToOutputSizesMap
-        )
+        ).first
 
         useCasesExpectedSizeMap.keys.forEach {
             val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
index d39f3e1..dba781b 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
@@ -33,7 +33,6 @@
 import android.graphics.ImageFormat;
 import android.graphics.Matrix;
 import android.graphics.Rect;
-import android.os.Build;
 import android.provider.MediaStore;
 import android.util.Rational;
 import android.util.Size;
@@ -50,6 +49,7 @@
 import androidx.camera.core.impl.UseCaseConfigFactory;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
+import androidx.camera.testing.CoreAppTestUtil;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraCaptureResult;
 import androidx.camera.testing.fakes.FakeCameraControl;
@@ -101,9 +101,7 @@
 
     @Before
     public void setup() {
-        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes"
-                        + " the test failure.",
-                Build.MODEL.equalsIgnoreCase("wembley") && Build.VERSION.SDK_INT <= 30);
+        CoreAppTestUtil.assumeCompatibleDevice();
         FakeCamera fakeCamera = new FakeCamera("fakeCameraId");
 
         FakeCameraDeviceSurfaceManager fakeCameraDeviceSurfaceManager =
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java
index 28e1e0f..a38bb27 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.hardware.camera2.CameraDevice;
+import android.util.Range;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
@@ -276,6 +277,50 @@
         configuration.getCameraCaptureCallbacks().add(mock(CameraCaptureCallback.class));
     }
 
+    @Test
+    public void builderChange_doNotChangeEarlierBuiltInstance() {
+        // 1. Arrange
+        CameraCaptureCallback callback1 = mock(CameraCaptureCallback.class);
+        CameraCaptureCallback callback2 = mock(CameraCaptureCallback.class);
+        DeferrableSurface deferrableSurface1 = mock(DeferrableSurface.class);
+        DeferrableSurface deferrableSurface2 = mock(DeferrableSurface.class);
+        Range<Integer> fpsRange1 = new Range<>(30, 30);
+        Range<Integer> fpsRange2 = new Range<>(15, 30);
+        int optionValue1 = 1;
+        int optionValue2 = 2;
+        int tagValue1 = 1;
+        int tagValue2 = 2;
+        int template1 = CameraDevice.TEMPLATE_PREVIEW;
+        int template2 = CameraDevice.TEMPLATE_RECORD;
+
+        CaptureConfig.Builder builder = new CaptureConfig.Builder();
+        builder.addSurface(deferrableSurface1);
+        builder.setExpectedFrameRateRange(fpsRange1);
+        builder.addCameraCaptureCallback(callback1);
+        builder.setTemplateType(template1);
+        builder.addTag("KEY", tagValue1);
+        builder.addImplementationOption(OPTION, optionValue1);
+        CaptureConfig captureConfig = builder.build();
+
+        // 2. Act
+        // builder change should not affect the instance built earlier.
+        builder.addSurface(deferrableSurface2);
+        builder.setExpectedFrameRateRange(fpsRange2);
+        builder.addCameraCaptureCallback(callback2);
+        builder.setTemplateType(template2);
+        builder.addTag("KEY", tagValue2);
+        builder.addImplementationOption(OPTION, optionValue2);
+
+        // 3. Verify
+        assertThat(captureConfig.getSurfaces()).containsExactly(deferrableSurface1);
+        assertThat(captureConfig.getExpectedFrameRateRange()).isEqualTo(fpsRange1);
+        assertThat(captureConfig.getCameraCaptureCallbacks()).containsExactly(callback1);
+        assertThat(captureConfig.getTemplateType()).isEqualTo(template1);
+        assertThat(captureConfig.getTagBundle().getTag("KEY")).isEqualTo(tagValue1);
+        assertThat(captureConfig.getImplementationOptions().retrieveOption(OPTION))
+                .isEqualTo(optionValue1);
+    }
+
     /**
      * A fake {@link MultiValueSet}.
      */
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java
index e82f563..e326787 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java
@@ -840,6 +840,134 @@
         assertThat(tag.getTag("TEST01")).isEqualTo("String");
     }
 
+    @Test
+    public void builderChange_doNotChangeEarlierBuiltInstance() {
+        // 1. Arrange
+        CameraCaptureCallback callback1 = mock(CameraCaptureCallback.class);
+        CameraCaptureCallback callback2 = mock(CameraCaptureCallback.class);
+        DeferrableSurface deferrableSurface1 = mock(DeferrableSurface.class);
+        DeferrableSurface deferrableSurface2 = mock(DeferrableSurface.class);
+        CameraDevice.StateCallback deviceStateCallback1 = mock(CameraDevice.StateCallback.class);
+        CameraDevice.StateCallback deviceStateCallback2 = mock(CameraDevice.StateCallback.class);
+        CameraCaptureSession.StateCallback sessionCallback1 =
+                mock(CameraCaptureSession.StateCallback.class);
+        CameraCaptureSession.StateCallback sessionCallback2 =
+                mock(CameraCaptureSession.StateCallback.class);
+        SessionConfig.ErrorListener errorListener1 = mock(SessionConfig.ErrorListener.class);
+        SessionConfig.ErrorListener errorListener2 = mock(SessionConfig.ErrorListener.class);
+        Range<Integer> fpsRange1 = new Range<>(30, 30);
+        Range<Integer> fpsRange2 = new Range<>(15, 30);
+        MutableOptionsBundle optionsBundle1 = MutableOptionsBundle.create();
+        optionsBundle1.insertOption(OPTION, 1);
+        MutableOptionsBundle optionsBundle2 = MutableOptionsBundle.create();
+        optionsBundle2.insertOption(OPTION, 2);
+        int template1 = CameraDevice.TEMPLATE_PREVIEW;
+        int template2 = CameraDevice.TEMPLATE_RECORD;
+
+        SessionConfig.Builder builder = new SessionConfig.Builder();
+        builder.addSurface(deferrableSurface1);
+        builder.setExpectedFrameRateRange(fpsRange1);
+        builder.addCameraCaptureCallback(callback1);
+        builder.addRepeatingCameraCaptureCallback(callback1);
+        builder.addDeviceStateCallback(deviceStateCallback1);
+        builder.addSessionStateCallback(sessionCallback1);
+        builder.setTemplateType(template1);
+        builder.addImplementationOptions(optionsBundle1);
+        builder.addErrorListener(errorListener1);
+        SessionConfig sessionConfig = builder.build();
+
+        // 2. Act
+        // builder change should not affect the instance built earlier.
+        builder.addSurface(deferrableSurface2);
+        builder.setExpectedFrameRateRange(fpsRange2);
+        builder.addCameraCaptureCallback(callback2);
+        builder.addRepeatingCameraCaptureCallback(callback2);
+        builder.addDeviceStateCallback(deviceStateCallback2);
+        builder.addSessionStateCallback(sessionCallback2);
+        builder.setTemplateType(template2);
+        builder.addImplementationOptions(optionsBundle2);
+        builder.addErrorListener(errorListener2);
+
+        // 3. Verify
+        assertThat(sessionConfig.getSurfaces()).containsExactly(deferrableSurface1);
+        assertThat(sessionConfig.getExpectedFrameRateRange()).isEqualTo(fpsRange1);
+        assertThat(sessionConfig.getSingleCameraCaptureCallbacks()).containsExactly(callback1);
+        assertThat(sessionConfig.getRepeatingCaptureConfig().getCameraCaptureCallbacks())
+                .containsExactly(callback1);
+        assertThat(sessionConfig.getDeviceStateCallbacks()).containsExactly(deviceStateCallback1);
+        assertThat(sessionConfig.getSessionStateCallbacks()).containsExactly(sessionCallback1);
+        assertThat(sessionConfig.getTemplateType()).isEqualTo(template1);
+        assertThat(sessionConfig.getImplementationOptions().retrieveOption(OPTION)).isEqualTo(1);
+        assertThat(sessionConfig.getErrorListeners()).containsExactly(errorListener1);
+    }
+
+    @Test
+    public void validatingBuilderChange_doNotChangeEarlierBuiltInstance() {
+        // 1. Arrange
+        CameraCaptureCallback callback1 = mock(CameraCaptureCallback.class);
+        CameraCaptureCallback callback2 = mock(CameraCaptureCallback.class);
+        DeferrableSurface deferrableSurface1 = mock(DeferrableSurface.class);
+        DeferrableSurface deferrableSurface2 = mock(DeferrableSurface.class);
+        CameraDevice.StateCallback deviceStateCallback1 = mock(CameraDevice.StateCallback.class);
+        CameraDevice.StateCallback deviceStateCallback2 = mock(CameraDevice.StateCallback.class);
+        CameraCaptureSession.StateCallback sessionCallback1 =
+                mock(CameraCaptureSession.StateCallback.class);
+        CameraCaptureSession.StateCallback sessionCallback2 =
+                mock(CameraCaptureSession.StateCallback.class);
+        SessionConfig.ErrorListener errorListener1 = mock(SessionConfig.ErrorListener.class);
+        SessionConfig.ErrorListener errorListener2 = mock(SessionConfig.ErrorListener.class);
+        Range<Integer> fpsRange1 = new Range<>(30, 30);
+        Range<Integer> fpsRange2 = new Range<>(15, 30);
+        MutableOptionsBundle optionsBundle1 = MutableOptionsBundle.create();
+        optionsBundle1.insertOption(OPTION, 1);
+        MutableOptionsBundle optionsBundle2 = MutableOptionsBundle.create();
+        optionsBundle2.insertOption(OPTION, 2);
+        int template1 = CameraDevice.TEMPLATE_PREVIEW;
+        int template2 = CameraDevice.TEMPLATE_RECORD;
+
+        SessionConfig.Builder builder = new SessionConfig.Builder();
+        builder.addSurface(deferrableSurface1);
+        builder.setExpectedFrameRateRange(fpsRange1);
+        builder.addCameraCaptureCallback(callback1);
+        builder.addRepeatingCameraCaptureCallback(callback1);
+        builder.addDeviceStateCallback(deviceStateCallback1);
+        builder.addSessionStateCallback(sessionCallback1);
+        builder.setTemplateType(template1);
+        builder.addImplementationOptions(optionsBundle1);
+        builder.addErrorListener(errorListener1);
+
+        SessionConfig.ValidatingBuilder validatingBuilder = new SessionConfig.ValidatingBuilder();
+        validatingBuilder.add(builder.build());
+        SessionConfig sessionConfig = validatingBuilder.build();
+
+        // 2. Act
+        // add another SessionConfig to ValidatingBuilder. This should not affect the
+        // instance built earlier.
+        SessionConfig.Builder builder2 = new SessionConfig.Builder();
+        builder2.addSurface(deferrableSurface2);
+        builder2.setExpectedFrameRateRange(fpsRange2);
+        builder2.addCameraCaptureCallback(callback2);
+        builder2.addRepeatingCameraCaptureCallback(callback2);
+        builder2.addDeviceStateCallback(deviceStateCallback2);
+        builder2.addSessionStateCallback(sessionCallback2);
+        builder2.setTemplateType(template2);
+        builder2.addImplementationOptions(optionsBundle2);
+        builder2.addErrorListener(errorListener2);
+        validatingBuilder.add(builder2.build());
+
+        // 3. Verify
+        assertThat(sessionConfig.getSurfaces()).containsExactly(deferrableSurface1);
+        assertThat(sessionConfig.getExpectedFrameRateRange()).isEqualTo(fpsRange1);
+        assertThat(sessionConfig.getSingleCameraCaptureCallbacks()).containsExactly(callback1);
+        assertThat(sessionConfig.getRepeatingCaptureConfig().getCameraCaptureCallbacks())
+                .containsExactly(callback1);
+        assertThat(sessionConfig.getDeviceStateCallbacks()).containsExactly(deviceStateCallback1);
+        assertThat(sessionConfig.getSessionStateCallbacks()).containsExactly(sessionCallback1);
+        assertThat(sessionConfig.getTemplateType()).isEqualTo(template1);
+        assertThat(sessionConfig.getImplementationOptions().retrieveOption(OPTION)).isEqualTo(1);
+        assertThat(sessionConfig.getErrorListeners()).containsExactly(errorListener1);
+    }
+
     private SessionConfig createSessionConfigWithTag(String key, Object tagValue) {
         SessionConfig.Builder builder1 = new SessionConfig.Builder();
         builder1.addSurface(mMockSurface1);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index b7268b0..8c70299 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -22,6 +22,7 @@
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_ONE_PIXEL_SHIFT_ENABLED;
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_FORMAT;
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_ROTATION_ENABLED;
+import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_DEFAULT_RESOLUTION;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
@@ -70,6 +71,7 @@
 import androidx.camera.core.impl.ConfigProvider;
 import androidx.camera.core.impl.DeferrableSurface;
 import androidx.camera.core.impl.ImageAnalysisConfig;
+import androidx.camera.core.impl.ImageInputConfig;
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
 import androidx.camera.core.impl.ImmediateSurface;
@@ -95,6 +97,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 
@@ -217,6 +220,9 @@
     // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached.
     ////////////////////////////////////////////////////////////////////////////////////////////
 
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    SessionConfig.Builder mSessionConfigBuilder;
+
     @Nullable
     private DeferrableSurface mDeferrableSurface;
 
@@ -370,6 +376,9 @@
 
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config,
                 streamSpec.getResolution());
+        if (streamSpec.getImplementationOptions() != null) {
+            sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
 
         if (mDeferrableSurface != null) {
             mDeferrableSurface.close();
@@ -387,8 +396,7 @@
 
         sessionConfigBuilder.setExpectedFrameRateRange(streamSpec.getExpectedFrameRateRange());
 
-        sessionConfigBuilder.addSurface(mDeferrableSurface);
-
+        sessionConfigBuilder.addSurface(mDeferrableSurface, streamSpec.getDynamicRange());
 
         sessionConfigBuilder.addErrorListener((sessionConfig, error) -> {
             clearPipeline();
@@ -814,14 +822,26 @@
     protected StreamSpec onSuggestedStreamSpecUpdated(@NonNull StreamSpec suggestedStreamSpec) {
         final ImageAnalysisConfig config = (ImageAnalysisConfig) getCurrentConfig();
 
-        SessionConfig.Builder sessionConfigBuilder = createPipeline(getCameraId(), config,
+        mSessionConfigBuilder = createPipeline(getCameraId(), config,
                 suggestedStreamSpec);
-        updateSessionConfig(sessionConfigBuilder.build());
+        updateSessionConfig(mSessionConfigBuilder.build());
 
         return suggestedStreamSpec;
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    protected StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(@NonNull Config config) {
+        mSessionConfigBuilder.addImplementationOptions(config);
+        updateSessionConfig(mSessionConfigBuilder.build());
+        return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
+    }
+
+    /**
      * Updates relative rotation if attached to a camera. No-op otherwise.
      */
     private void tryUpdateRelativeRotation() {
@@ -1009,6 +1029,15 @@
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 1;
         private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3;
 
+        /**
+         * Explicitly setting the default dynamic range to SDR (rather than UNSPECIFIED) means
+         * ImageAnalysis won't inherit dynamic ranges from other use cases.
+         */
+        // TODO(b/258099919): ImageAnalysis currently can't support HDR, so we don't expose the
+        //  dynamic range setter and require SDR. We may want to get rid of this default once we
+        //  can support tone-mapping from HDR -> SDR
+        private static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR;
+
         private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR =
                 new ResolutionSelector.Builder().setAspectRatioStrategy(
                         AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY).setResolutionStrategy(
@@ -1024,7 +1053,8 @@
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
                     .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
-                    .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS);
+                    .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS)
+                    .setDynamicRange(DEFAULT_DYNAMIC_RANGE);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -1041,7 +1071,8 @@
     public static final class Builder
             implements ImageOutputConfig.Builder<Builder>,
             ThreadConfig.Builder<Builder>,
-            UseCaseConfig.Builder<ImageAnalysis, ImageAnalysisConfig, Builder> {
+            UseCaseConfig.Builder<ImageAnalysis, ImageAnalysisConfig, Builder>,
+            ImageInputConfig.Builder<Builder> {
 
         private final MutableOptionsBundle mMutableConfig;
 
@@ -1610,5 +1641,29 @@
             getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
             return this;
         }
+
+        // Implementations of ImageInputConfig.Builder default methods
+
+        /**
+         * Sets the {@link DynamicRange}.
+         *
+         * <p>This is currently only exposed to internally set the dynamic range to SDR.
+         * @return The current Builder.
+         * @see DynamicRange
+         */
+        @RestrictTo(Scope.LIBRARY)
+        @NonNull
+        @Override
+        public Builder setDynamicRange(@NonNull DynamicRange dynamicRange) {
+            // TODO(b/258099919): ImageAnalysis currently can't support HDR, so we require SDR.
+            //  It's possible to support other DynamicRanges through tone-mapping or by exposing
+            //  other ImageReader formats, such as YCBCR_P010.
+            if (!Objects.equals(DynamicRange.SDR, dynamicRange)) {
+                throw new UnsupportedOperationException(
+                        "ImageAnalysis currently only supports SDR");
+            }
+            getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index b547104..3152b85 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -38,6 +38,7 @@
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_ROTATION;
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_CASE_EVENT_CALLBACK;
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_SOFTWARE_JPEG_ENCODER;
+import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
@@ -100,6 +101,7 @@
 import androidx.camera.core.impl.ConfigProvider;
 import androidx.camera.core.impl.DeferrableSurface;
 import androidx.camera.core.impl.ImageCaptureConfig;
+import androidx.camera.core.impl.ImageInputConfig;
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
 import androidx.camera.core.impl.ImageReaderProxy;
@@ -145,6 +147,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CancellationException;
@@ -382,6 +385,10 @@
             getCameraControl().addZslConfig(sessionConfigBuilder);
         }
 
+        if (streamSpec.getImplementationOptions() != null) {
+            sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
+
         // Setup the ImageReader to do processing
         Size resolution = streamSpec.getResolution();
         if (config.getImageReaderProxyProvider() != null) {
@@ -1610,6 +1617,18 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    protected StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(@NonNull Config config) {
+        mSessionConfigBuilder.addImplementationOptions(config);
+        updateSessionConfig(mSessionConfigBuilder.build());
+        return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
+    }
+
+    /**
      * Initiates a set of captures that will be used to create the output of
      * {@link #takePicture(OutputFileOptions, Executor, OnImageSavedCallback)} and its variants.
      *
@@ -1733,6 +1752,9 @@
         if (Build.VERSION.SDK_INT >= 23 && getCaptureMode() == CAPTURE_MODE_ZERO_SHUTTER_LAG) {
             getCameraControl().addZslConfig(sessionConfigBuilder);
         }
+        if (streamSpec.getImplementationOptions() != null) {
+            sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
         sessionConfigBuilder.addErrorListener((sessionConfig, error) -> {
             // TODO(b/143915543): Ensure this never gets called by a camera that is not attached
             //  to this use case so we don't need to do this check.
@@ -2020,13 +2042,17 @@
                         ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build();
 
         private static final ImageCaptureConfig DEFAULT_CONFIG;
+        // ImageCapture does not yet support HDR so we must default to SDR. This ensures it won't
+        // choose an HDR format when other use cases have selected HDR.
+        private static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR;
 
         static {
             Builder builder = new Builder()
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
                     .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
-                    .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE);
+                    .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
+                    .setDynamicRange(DEFAULT_DYNAMIC_RANGE);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -2512,7 +2538,8 @@
     public static final class Builder implements
             UseCaseConfig.Builder<ImageCapture, ImageCaptureConfig, Builder>,
             ImageOutputConfig.Builder<Builder>,
-            IoConfig.Builder<Builder> {
+            IoConfig.Builder<Builder>,
+            ImageInputConfig.Builder<Builder> {
 
         private final MutableOptionsBundle mMutableConfig;
 
@@ -3090,5 +3117,26 @@
             getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
             return this;
         }
+
+        // Implementations of ImageInputConfig.Builder default methods
+
+        /**
+         * Sets the {@link DynamicRange}.
+         *
+         * <p>This is currently only exposed to internally set the dynamic range to SDR.
+         * @return The current Builder.
+         * @see DynamicRange
+         */
+        @RestrictTo(Scope.LIBRARY)
+        @NonNull
+        @Override
+        public Builder setDynamicRange(@NonNull DynamicRange dynamicRange) {
+            // TODO(b/280893255): ImageCapture currently does not support HDR.
+            if (!Objects.equals(DynamicRange.SDR, dynamicRange)) {
+                throw new UnsupportedOperationException("ImageCapture currently only supports SDR");
+            }
+            getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 153f731..e593232 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -180,6 +180,9 @@
     // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached.
     ////////////////////////////////////////////////////////////////////////////////////////////
 
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    SessionConfig.Builder mSessionConfigBuilder;
+
     // TODO(b/259308680): remove mSessionDeferrableSurface and rely on mCameraEdge to get the
     //  DeferrableSurface
     private DeferrableSurface mSessionDeferrableSurface;
@@ -244,6 +247,9 @@
         mSessionDeferrableSurface = surfaceRequest.getDeferrableSurface();
         addCameraSurfaceAndErrorListener(sessionConfigBuilder, cameraId, config, streamSpec);
         sessionConfigBuilder.setExpectedFrameRateRange(streamSpec.getExpectedFrameRateRange());
+        if (streamSpec.getImplementationOptions() != null) {
+            sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
         return sessionConfigBuilder;
     }
 
@@ -298,6 +304,9 @@
         // Send the camera Surface to the camera2.
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config,
                 streamSpec.getResolution());
+        if (streamSpec.getImplementationOptions() != null) {
+            sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
         addCameraSurfaceAndErrorListener(sessionConfigBuilder, cameraId, config, streamSpec);
         return sessionConfigBuilder;
     }
@@ -368,7 +377,8 @@
         // SurfaceProcessorNode and CaptureProcessor cases, since no surface provider also means no
         // output target for these two cases.
         if (mSurfaceProvider != null) {
-            sessionConfigBuilder.addSurface(mSessionDeferrableSurface);
+            sessionConfigBuilder.addSurface(mSessionDeferrableSurface,
+                    streamSpec.getDynamicRange());
         }
 
         sessionConfigBuilder.addErrorListener((sessionConfig, error) -> {
@@ -516,7 +526,8 @@
 
     private void updateConfigAndOutput(@NonNull String cameraId, @NonNull PreviewConfig config,
             @NonNull StreamSpec streamSpec) {
-        updateSessionConfig(createPipeline(cameraId, config, streamSpec).build());
+        mSessionConfigBuilder = createPipeline(cameraId, config, streamSpec);
+        updateSessionConfig(mSessionConfigBuilder.build());
     }
 
     /**
@@ -643,6 +654,19 @@
     /**
      * {@inheritDoc}
      */
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    protected StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(@NonNull Config config) {
+        mSessionConfigBuilder.addImplementationOptions(config);
+        updateSessionConfig(mSessionConfigBuilder.build());
+        return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     */
     @Override
     @RestrictTo(Scope.LIBRARY)
     public void setViewPortCropRect(@NonNull Rect viewPortCropRect) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index 5b381e7..acdce0c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -701,6 +701,32 @@
     }
 
     /**
+     * Update the implementation options of the stream specification for the UseCase.
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public void updateSuggestedStreamSpecImplementationOptions(@NonNull Config config) {
+        mAttachedStreamSpec = onSuggestedStreamSpecImplementationOptionsUpdated(config);
+    }
+
+    /**
+     * Called when updating the stream specifications' implementation options of existing use cases
+     * via {@code CameraUseCaseAdapter#updateUseCases}.
+     *
+     * @param config The new implementationOptions for the stream specification.
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    protected StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(@NonNull Config config) {
+        if (mAttachedStreamSpec == null) {
+            throw new UnsupportedOperationException("Attempt to update the implementation options "
+                    + "for a use case without attached stream specifications.");
+        }
+        return mAttachedStreamSpec.toBuilder().setImplementationOptions(config).build();
+    }
+
+
+    /**
      * Called when CameraControlInternal is attached into the UseCase. UseCase may need to
      * override this method to configure the CameraControlInternal here. Ex. Setting correct flash
      * mode by CameraControlInternal.setFlashMode to enable correct AE mode and flash state.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
index b0c0a2a..9abb314 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
@@ -17,6 +17,7 @@
 package androidx.camera.core.impl;
 
 import android.content.Context;
+import android.util.Pair;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
@@ -42,8 +43,8 @@
         /**
          * Creates a new, initialized instance of a CameraDeviceSurfaceManager.
          *
-         * @param context the android context
-         * @param cameraManager the camera manager object used to query the camera information.
+         * @param context            the android context
+         * @param cameraManager      the camera manager object used to query the camera information.
          * @param availableCameraIds current available camera ids.
          * @return the factory instance
          * @throws InitializationException if it fails to create the factory
@@ -90,7 +91,8 @@
      *                                  is not a valid id.
      */
     @NonNull
-    Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
+    Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
+            getSuggestedStreamSpecs(
             @CameraMode.Mode int cameraMode,
             @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java
index 1485065..6cf83bf 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java
@@ -416,7 +416,7 @@
                     OptionsBundle.from(mImplementationOptions),
                     mTemplateType,
                     mExpectedFrameRateRange,
-                    mCameraCaptureCallbacks,
+                    new ArrayList<>(mCameraCaptureCallbacks),
                     mUseRepeatingSurface,
                     TagBundle.from(mMutableTagBundle),
                     mCameraCaptureResult);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
index 91b520a..1ba7e29 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
@@ -694,10 +694,10 @@
         public SessionConfig build() {
             return new SessionConfig(
                     new ArrayList<>(mOutputConfigs),
-                    mDeviceStateCallbacks,
-                    mSessionStateCallbacks,
-                    mSingleCameraCaptureCallbacks,
-                    mErrorListeners,
+                    new ArrayList<>(mDeviceStateCallbacks),
+                    new ArrayList<>(mSessionStateCallbacks),
+                    new ArrayList<>(mSingleCameraCaptureCallbacks),
+                    new ArrayList<>(mErrorListeners),
                     mCaptureConfigBuilder.build(),
                     mInputConfiguration);
         }
@@ -847,10 +847,10 @@
 
             return new SessionConfig(
                     outputConfigs,
-                    mDeviceStateCallbacks,
-                    mSessionStateCallbacks,
-                    mSingleCameraCaptureCallbacks,
-                    mErrorListeners,
+                    new ArrayList<>(mDeviceStateCallbacks),
+                    new ArrayList<>(mSessionStateCallbacks),
+                    new ArrayList<>(mSingleCameraCaptureCallbacks),
+                    new ArrayList<>(mErrorListeners),
                     mCaptureConfigBuilder.build(),
                     mInputConfiguration);
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 2350fbc..7d284bd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -31,6 +31,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
+import android.util.Pair;
 import android.util.Size;
 import android.view.Surface;
 
@@ -62,6 +63,7 @@
 import androidx.camera.core.impl.RestrictedCameraControl;
 import androidx.camera.core.impl.RestrictedCameraControl.CameraOperation;
 import androidx.camera.core.impl.RestrictedCameraInfo;
+import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.SessionProcessor;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceConfig;
@@ -79,6 +81,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -334,6 +337,22 @@
             }
             mCameraInternal.detachUseCases(cameraUseCasesToDetach);
 
+            // Update StreamSpec for UseCases to keep.
+            if (!cameraUseCasesToDetach.isEmpty()) {
+                // Only do this if we are not removing UseCase, because updating SessionConfig
+                // when removing UseCases may lead to flickering.
+                for (UseCase useCase : cameraUseCasesToKeep) {
+                    if (suggestedStreamSpecMap.containsKey(useCase)) {
+                        StreamSpec newStreamSpec = suggestedStreamSpecMap.get(useCase);
+                        Config config = newStreamSpec.getImplementationOptions();
+                        if (config != null && hasImplementationOptionChanged(newStreamSpec,
+                                useCase.getSessionConfig())) {
+                            useCase.updateSuggestedStreamSpecImplementationOptions(config);
+                        }
+                    }
+                }
+            }
+
             // Attach new UseCases.
             for (UseCase useCase : cameraUseCasesToAttach) {
                 ConfigPair configPair = requireNonNull(configs.get(useCase));
@@ -361,6 +380,29 @@
         }
     }
 
+    /**
+     * Return true if the given StreamSpec has any option with a different value than that
+     * of the given sessionConfig.
+     */
+    private static boolean hasImplementationOptionChanged(
+            StreamSpec streamSpec,
+            SessionConfig sessionConfig) {
+        Config newStreamSpecOptions = streamSpec.getImplementationOptions();
+        Config sessionConfigOptions = sessionConfig.getImplementationOptions();
+        if (newStreamSpecOptions.listOptions().size()
+                != sessionConfig.getImplementationOptions().listOptions().size()) {
+            return true;
+        }
+        for (Config.Option<?> newOption : newStreamSpecOptions.listOptions()) {
+            if (!sessionConfigOptions.containsOption(newOption)
+                    || !Objects.equals(sessionConfigOptions.retrieveOption(newOption),
+                    newStreamSpecOptions.retrieveOption(newOption))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private @CameraMode.Mode int getCameraMode() {
         synchronized (mLock) {
             if (mCameraCoordinator.getCameraOperatingMode()
@@ -597,6 +639,7 @@
         List<AttachedSurfaceInfo> existingSurfaces = new ArrayList<>();
         String cameraId = cameraInfoInternal.getCameraId();
         Map<UseCase, StreamSpec> suggestedStreamSpecs = new HashMap<>();
+        Map<AttachedSurfaceInfo, UseCase> surfaceInfoUseCaseMap = new HashMap<>();
 
         // Get resolution for current use cases.
         for (UseCase useCase : currentUseCases) {
@@ -606,12 +649,14 @@
                             cameraId,
                             useCase.getImageFormat(),
                             useCase.getAttachedSurfaceResolution());
-            existingSurfaces.add(AttachedSurfaceInfo.create(surfaceConfig,
+            AttachedSurfaceInfo attachedSurfaceInfo = AttachedSurfaceInfo.create(surfaceConfig,
                     useCase.getImageFormat(), useCase.getAttachedSurfaceResolution(),
                     Preconditions.checkNotNull(useCase.getAttachedStreamSpec()).getDynamicRange(),
                     getCaptureTypes(useCase),
                     useCase.getAttachedStreamSpec().getImplementationOptions(),
-                    useCase.getCurrentConfig().getTargetFrameRate(null)));
+                    useCase.getCurrentConfig().getTargetFrameRate(null));
+            existingSurfaces.add(attachedSurfaceInfo);
+            surfaceInfoUseCaseMap.put(attachedSurfaceInfo, useCase);
             suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec());
         }
 
@@ -643,7 +688,8 @@
             }
 
             // Get suggested stream specifications and update the use case session configuration
-            Map<UseCaseConfig<?>, StreamSpec> useCaseConfigStreamSpecMap =
+            Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
+                    streamSpecMaps =
                     mCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                             cameraMode,
                             cameraId, existingSurfaces,
@@ -651,7 +697,14 @@
 
             for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) {
                 suggestedStreamSpecs.put(entry.getValue(),
-                        useCaseConfigStreamSpecMap.get(entry.getKey()));
+                        streamSpecMaps.first.get(entry.getKey()));
+            }
+            for (Map.Entry<AttachedSurfaceInfo, StreamSpec> entry :
+                    streamSpecMaps.second.entrySet()) {
+                if (surfaceInfoUseCaseMap.containsKey(entry.getKey())) {
+                    suggestedStreamSpecs.put(surfaceInfoUseCaseMap.get(entry.getKey()),
+                            entry.getValue());
+                }
             }
         }
         return suggestedStreamSpecs;
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 93cd629..830387a 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
@@ -36,6 +36,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.UseCase;
@@ -86,6 +87,9 @@
     @Nullable
     private SurfaceEdge mSharingInputEdge;
 
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    SessionConfig.Builder mSessionConfigBuilder;
+
     static {
         MutableConfig mutableConfig = new StreamSharingBuilder().getMutableConfig();
         mutableConfig.insertOption(OPTION_INPUT_FORMAT,
@@ -156,6 +160,18 @@
         return streamSpec;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @NonNull
+    @Override
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    protected StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(@NonNull Config config) {
+        mSessionConfigBuilder.addImplementationOptions(config);
+        updateSessionConfig(mSessionConfigBuilder.build());
+        return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
+    }
+
     @Override
     public void onBind() {
         super.onBind();
@@ -239,7 +255,11 @@
                 streamSpec.getResolution());
         builder.addSurface(mCameraEdge.getDeferrableSurface());
         builder.addRepeatingCameraCaptureCallback(mVirtualCamera.getParentMetadataCallback());
+        if (streamSpec.getImplementationOptions() != null) {
+            builder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
         addCameraErrorListener(builder, cameraId, config, streamSpec);
+        mSessionConfigBuilder = builder;
         return builder.build();
     }
 
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index 3195cae..dd4cd17 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -38,6 +38,7 @@
 import androidx.camera.core.impl.CameraFactory;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.ImageAnalysisConfig;
+import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.TagBundle;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
@@ -91,6 +92,8 @@
     private static final long TIMESTAMP_1 = 1;
     private static final long TIMESTAMP_2 = 2;
     private static final long TIMESTAMP_3 = 3;
+    public static final androidx.camera.core.impl.Config.Option<Integer> TEST_OPTION =
+            androidx.camera.core.impl.Config.Option.create("test.testOption", int.class);
 
     private Handler mCallbackHandler;
     private Handler mBackgroundHandler;
@@ -438,6 +441,20 @@
         assertCanReceiveAnalysisImage(mImageAnalysis);
     }
 
+    @Test
+    public void sessionConfigHasStreamSpecImplementationOptions_whenUpdateStreamSpecImplOptions()
+            throws CameraUseCaseAdapter.CameraException {
+        setUpImageAnalysisWithStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST);
+        int newImplementationOptionValue = 6;
+        MutableOptionsBundle streamSpecOptions = MutableOptionsBundle.create();
+        streamSpecOptions.insertOption(TEST_OPTION, newImplementationOptionValue);
+        mImageAnalysis.updateSuggestedStreamSpecImplementationOptions(streamSpecOptions);
+        assertThat(
+                mImageAnalysis.getSessionConfig().getImplementationOptions().retrieveOption(
+                        TEST_OPTION
+                )).isEqualTo(newImplementationOptionValue);
+    }
+
     @SuppressWarnings("deprecation") // test for legacy resolution API
     @Test
     public void throwException_whenSetBothTargetResolutionAndAspectRatio() {
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index ad923b7f1..0e822e5 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -39,6 +39,7 @@
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.Identifier
+import androidx.camera.core.impl.MutableOptionsBundle
 import androidx.camera.core.impl.OptionsBundle
 import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.SessionProcessor
@@ -116,6 +117,12 @@
         override fun onError(exception: ImageCaptureException) {
         }
     }
+    private val testImplementationOption: androidx.camera.core.impl.Config.Option<Int> =
+        androidx.camera.core.impl.Config.Option.create(
+            "test.testOption",
+            Int::class.javaPrimitiveType!!
+        )
+    private val testImplementationOptionValue = 5
 
     @Before
     @Throws(ExecutionException::class, InterruptedException::class)
@@ -626,6 +633,22 @@
         assertThat(cameraControl.isZslConfigAdded).isTrue()
     }
 
+    @Test
+    fun sessionConfigHasStreamSpecImplementationOptions_whenUpdateStreamSpecImplOptions() {
+        val imageCapture = bindImageCapture(
+            bufferFormat = ImageFormat.JPEG,
+        )
+        val newImplementationOptionValue = 6
+        val streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, newImplementationOptionValue)
+        imageCapture.updateSuggestedStreamSpecImplementationOptions(streamSpecOptions)
+        assertThat(
+            imageCapture.sessionConfig.implementationOptions.retrieveOption(
+                testImplementationOption
+            )
+        ).isEqualTo(newImplementationOptionValue)
+    }
+
     @Suppress("DEPRECATION") // test for legacy resolution API
     @Test
     fun throwException_whenSetBothTargetResolutionAndAspectRatio() {
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
index 6f8fa37..3b09a2a 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
@@ -35,6 +35,7 @@
 import androidx.camera.core.SurfaceRequest.TransformationInfo
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CameraThreadConfig
+import androidx.camera.core.impl.MutableOptionsBundle
 import androidx.camera.core.impl.OptionsBundle
 import androidx.camera.core.impl.PreviewConfig
 import androidx.camera.core.impl.SessionConfig
@@ -80,6 +81,8 @@
 @Config(
     minSdk = Build.VERSION_CODES.LOLLIPOP
 )
+// Option Declarations:
+// *********************************************************************************************
 class PreviewTest {
 
     private var cameraUseCaseAdapter: CameraUseCaseAdapter? = null
@@ -96,6 +99,13 @@
 
     private val handlersToRelease = mutableListOf<Handler>()
 
+    private val testImplementationOption: androidx.camera.core.impl.Config.Option<Int> =
+        androidx.camera.core.impl.Config.Option.create(
+            "test.testOption",
+            Int::class.javaPrimitiveType!!
+        )
+    private val testImplementationOptionValue = 5
+
     @Before
     @Throws(ExecutionException::class, InterruptedException::class)
     fun setUp() {
@@ -641,6 +651,30 @@
         assertThat(receivedAfterAttach).isTrue()
     }
 
+    @Test
+    fun sessionConfigHasStreamSpecImplementationOptions_whenCreatePipeline() {
+        val preview = createPreview(effect)
+        assertThat(
+            preview.sessionConfig.implementationOptions.retrieveOption(
+                testImplementationOption
+            )
+        ).isEqualTo(testImplementationOptionValue)
+    }
+
+    @Test
+    fun sessionConfigHasStreamSpecImplementationOptions_whenUpdateStreamSpecImplOptions() {
+        val preview = createPreview(effect)
+        val newImplementationOptionValue = 6
+        val streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, newImplementationOptionValue)
+        preview.updateSuggestedStreamSpecImplementationOptions(streamSpecOptions)
+        assertThat(
+            preview.sessionConfig.implementationOptions.retrieveOption(
+                testImplementationOption
+            )
+        ).isEqualTo(newImplementationOptionValue)
+    }
+
     @Suppress("DEPRECATION") // test for legacy resolution API
     @Test
     fun throwException_whenSetBothTargetResolutionAndAspectRatio() {
@@ -734,8 +768,11 @@
         )
         previewToDetach.bindToCamera(camera, null, previewConfig)
 
-        val streamSpec = StreamSpec.builder(Size(640, 480)).build()
-        previewToDetach.onSuggestedStreamSpecUpdated(streamSpec)
+        val streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, testImplementationOptionValue)
+        val streamSpec = StreamSpec.builder(Size(640, 480))
+            .setImplementationOptions(streamSpecOptions).build()
+        previewToDetach.updateSuggestedStreamSpec(streamSpec)
         return previewToDetach
     }
 
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 969f695..82b2799 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
@@ -29,6 +29,7 @@
 import androidx.camera.core.ImageProxy
 import androidx.camera.core.impl.CameraCaptureCallback
 import androidx.camera.core.impl.CameraCaptureResult
+import androidx.camera.core.impl.MutableOptionsBundle
 import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.StreamSpec
 import androidx.camera.core.impl.UseCaseConfig
@@ -80,6 +81,12 @@
     private lateinit var effectProcessor: FakeSurfaceProcessorInternal
     private lateinit var sharingProcessor: FakeSurfaceProcessorInternal
     private lateinit var effect: CameraEffect
+    private val testImplementationOption: androidx.camera.core.impl.Config.Option<Int> =
+        androidx.camera.core.impl.Config.Option.create(
+            "test.testOption",
+            Int::class.javaPrimitiveType!!
+        )
+    private val testImplementationOptionValue = 5
 
     @Before
     fun setUp() {
@@ -183,6 +190,57 @@
         assertThat(result2.getCompleted().tagBundle.getTag(key)).isEqualTo(value)
     }
 
+    @Test
+    fun sessionConfigHasStreamSpecImplementationOptions_whenCreatePipeline() {
+        // Arrange: set up StreamSharing with ImageCapture as child
+        val imageCapture = ImageCapture.Builder().build()
+        streamSharing = StreamSharing(camera, setOf(child1, imageCapture), useCaseConfigFactory)
+        streamSharing.bindToCamera(camera, null, defaultConfig)
+
+        // Act: update stream specification.
+        val streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, testImplementationOptionValue)
+        streamSharing.onSuggestedStreamSpecUpdated(
+            StreamSpec.builder(size).setImplementationOptions(streamSpecOptions).build()
+        )
+
+        // Assert: the session config gets the correct implementation options from stream
+        // specification.
+        assertThat(
+            streamSharing.sessionConfig.implementationOptions.retrieveOption(
+                testImplementationOption
+            )
+        ).isEqualTo(testImplementationOptionValue)
+    }
+
+    @Test
+    fun sessionConfigHasStreamSpecImplementationOptions_whenUpdateStreamSpecImplOptions() {
+        // Arrange: set up StreamSharing with ImageCapture as child with initial stream
+        // specification implementation options.
+        val imageCapture = ImageCapture.Builder().build()
+        streamSharing = StreamSharing(camera, setOf(child1, imageCapture), useCaseConfigFactory)
+        streamSharing.bindToCamera(camera, null, defaultConfig)
+        var streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, testImplementationOptionValue)
+        streamSharing.updateSuggestedStreamSpec(
+            StreamSpec.builder(size).setImplementationOptions(streamSpecOptions).build()
+        )
+
+        // Act: update stream specification implementation options.
+        val newImplementationOptionValue = 6
+        streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, newImplementationOptionValue)
+        streamSharing.updateSuggestedStreamSpecImplementationOptions(streamSpecOptions)
+
+        // Assert: the session config gets the correct implementation options from stream
+        // specification.
+        assertThat(
+            streamSharing.sessionConfig.implementationOptions.retrieveOption(
+                testImplementationOption
+            )
+        ).isEqualTo(newImplementationOptionValue)
+    }
+
     private fun FakeUseCase.setTagBundleOnSessionConfigAsync(
         key: String,
         value: String
diff --git a/camera/camera-extensions-stub/src/main/AndroidManifest.xml b/camera/camera-extensions-stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 9517533..0000000
--- a/camera/camera-extensions-stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
diff --git a/camera/camera-lifecycle/src/main/AndroidManifest.xml b/camera/camera-lifecycle/src/main/AndroidManifest.xml
deleted file mode 100644
index 39dd6cb..0000000
--- a/camera/camera-lifecycle/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<manifest />
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
index 7909211..5d9db0e 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
@@ -285,8 +285,10 @@
     @VisibleForTesting
     @NonNull
     public ListenableFuture<Void> shutdown() {
-        runOnMainSync(this::unbindAll);
-        mLifecycleCameraRepository.clear();
+        runOnMainSync(() -> {
+            unbindAll();
+            mLifecycleCameraRepository.clear();
+        });
 
         ListenableFuture<Void> shutdownFuture = mCameraX != null ? mCameraX.shutdown() :
                 Futures.immediateFuture(null);
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java
index 089ee92..13b1112 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.testing;
 
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.annotation.SuppressLint;
@@ -72,6 +73,10 @@
                 && Build.MODEL.contains("Nexus 5")) {
             throw new AssumptionViolatedException("Known issue, b/134894604.");
         }
+
+        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes"
+                        + " the test failure.",
+                Build.MODEL.equalsIgnoreCase("wembley") && Build.VERSION.SDK_INT <= 30);
     }
 
     /**
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
index b3ff181..bf73e16 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
@@ -23,6 +23,7 @@
 
 import static com.google.common.primitives.Ints.asList;
 
+import android.util.Pair;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
@@ -84,8 +85,10 @@
 
     @NonNull
     @Override
-    public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            @CameraMode.Mode int cameraMode, @NonNull String cameraId,
+    public Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
+            getSuggestedStreamSpecs(
+            @CameraMode.Mode int cameraMode,
+            @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
         List<UseCaseConfig<?>> newUseCaseConfigs =
@@ -106,7 +109,7 @@
             suggestedStreamSpecs.put(useCaseConfig, streamSpec);
         }
 
-        return suggestedStreamSpecs;
+        return new Pair<>(suggestedStreamSpecs, new HashMap<>());
     }
 
     /**
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
index 12a104a..8902d29 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
@@ -45,9 +45,11 @@
 public class FakeUseCaseConfig implements UseCaseConfig<FakeUseCase>, ImageOutputConfig {
 
     private final Config mConfig;
+    private final CaptureType mCaptureType;
 
-    FakeUseCaseConfig(Config config) {
+    FakeUseCaseConfig(Config config, CaptureType captureType) {
         mConfig = config;
+        mCaptureType = captureType;
     }
 
     @NonNull
@@ -62,6 +64,12 @@
                 INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
     }
 
+    @NonNull
+    @Override
+    public CaptureType getCaptureType() {
+        return mCaptureType;
+    }
+
     /** Builder for an empty Config */
     @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
     public static final class Builder implements
@@ -103,7 +111,7 @@
         @NonNull
         @Override
         public FakeUseCaseConfig getUseCaseConfig() {
-            return new FakeUseCaseConfig(OptionsBundle.from(mOptionsBundle));
+            return new FakeUseCaseConfig(OptionsBundle.from(mOptionsBundle), mCaptureType);
         }
 
         @Override
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeVideoEncoderInfo.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeVideoEncoderInfo.kt
index f1f27d5..ac8b164 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeVideoEncoderInfo.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeVideoEncoderInfo.kt
@@ -22,41 +22,41 @@
 
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 class FakeVideoEncoderInfo(
-    var _supportedWidths: Range<Int> = Range.create(0, Integer.MAX_VALUE),
-    var _supportedHeights: Range<Int> = Range.create(0, Integer.MAX_VALUE),
-    var _widthAlignment: Int = 2,
-    var _heightAlignment: Int = 2,
-    var _supportedBitrateRange: Range<Int> = Range(1, Int.MAX_VALUE)
+    @JvmField var supportedWidths: Range<Int> = Range.create(0, Integer.MAX_VALUE),
+    @JvmField var supportedHeights: Range<Int> = Range.create(0, Integer.MAX_VALUE),
+    @JvmField var widthAlignment: Int = 2,
+    @JvmField var heightAlignment: Int = 2,
+    @JvmField var supportedBitrateRange: Range<Int> = Range(1, Int.MAX_VALUE)
 ) : FakeEncoderInfo(), VideoEncoderInfo {
     override fun isSizeSupported(width: Int, height: Int) =
-        _supportedWidths.contains(width) && _supportedHeights.contains(height) &&
-            width.mod(_widthAlignment) == 0 && height.mod(_heightAlignment) == 0
+        supportedWidths.contains(width) && supportedHeights.contains(height) &&
+            width.mod(widthAlignment) == 0 && height.mod(heightAlignment) == 0
 
     override fun getSupportedWidths(): Range<Int> {
-        return _supportedWidths
+        return supportedWidths
     }
 
     override fun getSupportedHeights(): Range<Int> {
-        return _supportedHeights
+        return supportedHeights
     }
 
     override fun getSupportedWidthsFor(height: Int): Range<Int> {
-        return _supportedWidths
+        return supportedWidths
     }
 
     override fun getSupportedHeightsFor(width: Int): Range<Int> {
-        return _supportedHeights
+        return supportedHeights
     }
 
     override fun getWidthAlignment(): Int {
-        return _widthAlignment
+        return widthAlignment
     }
 
     override fun getHeightAlignment(): Int {
-        return _heightAlignment
+        return heightAlignment
     }
 
     override fun getSupportedBitrateRange(): Range<Int> {
-        return _supportedBitrateRange
+        return supportedBitrateRange
     }
 }
\ No newline at end of file
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
index b5ba2a4..d60a5de 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
@@ -132,12 +132,12 @@
                 mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                         CameraMode.DEFAULT,
                         FAKE_CAMERA_ID0,
-                        Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
+                        emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig)).first;
         Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecCamera1 =
                 mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                         CameraMode.DEFAULT,
                         FAKE_CAMERA_ID1,
-                        Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
+                        emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig)).first;
 
         assertThat(suggestedStreamSpecsCamera0.get(mFakeUseCaseConfig)).isEqualTo(
                 StreamSpec.builder(new Size(FAKE_WIDTH0, FAKE_HEIGHT0)).build());
diff --git a/camera/camera-testlib-extensions/src/main/AndroidManifest.xml b/camera/camera-testlib-extensions/src/main/AndroidManifest.xml
deleted file mode 100644
index 9517533..0000000
--- a/camera/camera-testlib-extensions/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
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 5a9196e..3f32f37 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
@@ -30,6 +30,7 @@
 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.Preview
 import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.impl.CameraInfoInternal
 import androidx.camera.core.impl.MutableStateObservable
@@ -115,6 +116,8 @@
             arrayOf(Camera2Config::class.simpleName, Camera2Config.defaultConfig()),
             arrayOf(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig())
         )
+
+        private val DYNAMIC_RANGE_HLG10 = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
     }
 
     private val context: Context = ApplicationProvider.getApplicationContext()
@@ -390,18 +393,45 @@
     @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)
+            cameraInfo.supportedDynamicRanges.contains(DYNAMIC_RANGE_HLG10)
         )
 
         testDynamicRangeSelection(
-            requestedDynamicRange = hlg10DynamicRange
+            requestedDynamicRange = DYNAMIC_RANGE_HLG10
         ) { selectedDynamicRange ->
-            assertThat(selectedDynamicRange).isEqualTo(hlg10DynamicRange)
+            assertThat(selectedDynamicRange).isEqualTo(DYNAMIC_RANGE_HLG10)
         }
     }
+    @SdkSuppress(minSdkVersion = 33) // HLG10 only supported on API 33+
+    @Test
+    fun dynamicRange_isSetInSessionConfig(): Unit = runBlocking {
+        // TODO(b/275632219): Disabled on camera-pipe until automatic dynamic range
+        //  selection is supported
+        assumeTrue(implName != CameraPipeConfig::class.simpleName)
+        assumeTrue(
+            "Device does not support HLG10",
+            cameraInfo.supportedDynamicRanges.contains(DYNAMIC_RANGE_HLG10)
+        )
+
+        // Arrange.
+        val videoOutput = createTestVideoOutput()
+        val videoCapture = VideoCapture.Builder(videoOutput)
+            .setDynamicRange(DYNAMIC_RANGE_HLG10)
+            .build()
+
+        // Act.
+        withContext(Dispatchers.Main) {
+            cameraUseCaseAdapter.addUseCases(listOf(videoCapture))
+        }
+
+        // Assert.
+        // Wait for surface request to ensure session config was attached
+        videoOutput.nextSurfaceRequest(5, TimeUnit.SECONDS)
+        val outputConfig = videoCapture.sessionConfig.outputConfigs.first()
+        assertThat(outputConfig.dynamicRange).isEqualTo(DYNAMIC_RANGE_HLG10)
+    }
 
     @SdkSuppress(minSdkVersion = 33) // 10-bit HDR only supported on API 33+
     @Test
@@ -421,6 +451,46 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = 33) // 10-bit HDR only supported on API 33+
+    @Test
+    fun dynamicRangeHlg_selectsAndAppliesHlgForConcurrentPreview(): Unit = runBlocking {
+        // TODO(b/275632219): Disabled on camera-pipe until automatic dynamic range
+        //  selection is supported
+        assumeTrue(implName != CameraPipeConfig::class.simpleName)
+        assumeTrue(
+            "Device does not support HLG10",
+            cameraInfo.supportedDynamicRanges.contains(DYNAMIC_RANGE_HLG10)
+        )
+
+        // Arrange.
+        val videoOutput = createTestVideoOutput()
+        val videoCapture = VideoCapture.Builder(videoOutput)
+            .setDynamicRange(DYNAMIC_RANGE_HLG10)
+            .build()
+        // Preview will derive dynamic range from VideoCapture since it uses
+        // DynamicRange.UNSPECIFIED by default.
+        val preview = Preview.Builder().build()
+
+        // Act.
+        val deferredSurfaceRequest = CompletableDeferred<SurfaceRequest>()
+        withContext(Dispatchers.Main) {
+            // SurfaceProvider will run on main thread
+            preview.setSurfaceProvider {
+                deferredSurfaceRequest.complete(it)
+            }
+            cameraUseCaseAdapter.addUseCases(listOf(videoCapture, preview))
+        }
+
+        // Assert.
+        val timeout = 5.seconds
+        val previewSurfaceRequest = withTimeoutOrNull(timeout) {
+             deferredSurfaceRequest.await()
+        } ?: fail("Timed out waiting for Preview SurfaceRequest. Waited $timeout.")
+        val previewOutputConfig = preview.sessionConfig.outputConfigs.first()
+        assertThat(previewSurfaceRequest.dynamicRange).isEqualTo(DYNAMIC_RANGE_HLG10)
+        assertThat(previewOutputConfig.dynamicRange).isEqualTo(DYNAMIC_RANGE_HLG10)
+    }
+
     private suspend fun testDynamicRangeSelection(
         requestedDynamicRange: DynamicRange? = null,
         assertBlock: (selectedDynamicRange: DynamicRange) -> Unit
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index 7e62cf4..39a9ec0 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -316,7 +316,7 @@
             completeVideoRecording(videoCapture, file)
 
             // Verify.
-            verifyVideoResolution(getExpectedResolution(videoCapture), file)
+            verifyVideoResolution(getExpectedResolution(videoCapture, targetResolution), file)
 
             // Cleanup.
             instrumentation.runOnMainSync {
@@ -410,7 +410,10 @@
         completeVideoRecording(videoCapture, file)
 
         // Verify.
-        verifyVideoResolution(getExpectedResolution(videoCapture), file)
+        verifyVideoResolution(
+            getExpectedResolution(videoCapture, rectToSize(videoCapture.cropRect!!)),
+            file
+        )
 
         // Cleanup.
         file.delete()
@@ -1024,11 +1027,10 @@
         }
     }
 
-    private fun getExpectedResolution(videoCapture: VideoCapture<Recorder>): Size =
-        rotateSize(
-            rectToSize(videoCapture.cropRect!!),
-            getExpectedRotation(videoCapture).contentRotation
-        )
+    private fun getExpectedResolution(
+        videoCapture: VideoCapture<Recorder>,
+        resolution: Size
+    ): Size = rotateSize(resolution, getExpectedRotation(videoCapture).contentRotation)
 
     private fun getExpectedAspectRatio(videoCapture: VideoCapture<Recorder>): Rational? {
         val needRotate by lazy { is90or270(getExpectedRotation(videoCapture).contentRotation) }
diff --git a/camera/camera-video/src/main/AndroidManifest.xml b/camera/camera-video/src/main/AndroidManifest.xml
deleted file mode 100644
index 9a5f4d9..0000000
--- a/camera/camera-video/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
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 8d75923..0a0373e 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
@@ -463,11 +463,13 @@
                 + "specification should be already updated and shouldn't be null.");
         Preconditions.checkState(mSurfaceRequest == null, "The surface request should be null "
                 + "when VideoCapture is attached.");
+        StreamSpec attachedStreamSpec = Preconditions.checkNotNull(getAttachedStreamSpec());
         mStreamInfo = fetchObservableValue(getOutput().getStreamInfo(),
                 StreamInfo.STREAM_INFO_ANY_INACTIVE);
         mSessionConfigBuilder = createPipeline(getCameraId(),
-                (VideoCaptureConfig<T>) getCurrentConfig(), getAttachedStreamSpec());
-        applyStreamInfoToSessionConfigBuilder(mSessionConfigBuilder, mStreamInfo);
+                (VideoCaptureConfig<T>) getCurrentConfig(), attachedStreamSpec);
+        applyStreamInfoAndStreamSpecToSessionConfigBuilder(mSessionConfigBuilder, mStreamInfo,
+                attachedStreamSpec);
         updateSessionConfig(mSessionConfigBuilder.build());
         // VideoCapture has to be active to apply SessionConfig's template type.
         notifyActive();
@@ -508,6 +510,18 @@
         clearPipeline();
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    protected StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(@NonNull Config config) {
+        mSessionConfigBuilder.addImplementationOptions(config);
+        updateSessionConfig(mSessionConfigBuilder.build());
+        return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -711,6 +725,9 @@
         if (USE_TEMPLATE_PREVIEW_BY_QUIRK) {
             sessionConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
         }
+        if (streamSpec.getImplementationOptions() != null) {
+            sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
+        }
 
         return sessionConfigBuilder;
     }
@@ -762,7 +779,8 @@
         if (isCurrentCamera(cameraId)) {
             // Only reset the pipeline when the bound camera is the same.
             mSessionConfigBuilder = createPipeline(cameraId, config, streamSpec);
-            applyStreamInfoToSessionConfigBuilder(mSessionConfigBuilder, mStreamInfo);
+            applyStreamInfoAndStreamSpecToSessionConfigBuilder(mSessionConfigBuilder, mStreamInfo,
+                    streamSpec);
             updateSessionConfig(mSessionConfigBuilder.build());
             notifyReset();
         }
@@ -861,6 +879,7 @@
             // Doing resetPipeline() includes notifyReset/notifyUpdated(). Doing NotifyReset()
             // includes notifyUpdated(). So we just take actions on higher order item for
             // optimization.
+            StreamSpec attachedStreamSpec = Preconditions.checkNotNull(getAttachedStreamSpec());
             if (!StreamInfo.NON_SURFACE_STREAM_ID.contains(currentStreamInfo.getId())
                     && !StreamInfo.NON_SURFACE_STREAM_ID.contains(streamInfo.getId())
                     && currentStreamInfo.getId() != streamInfo.getId()) {
@@ -874,11 +893,15 @@
                     && streamInfo.getId() != STREAM_ID_ERROR)) {
                 // If id switch to STREAM_ID_ERROR, it means VideoOutput is failed to setup video
                 // stream. The surface should be removed from camera. Vice versa.
-                applyStreamInfoToSessionConfigBuilder(mSessionConfigBuilder, streamInfo);
+                applyStreamInfoAndStreamSpecToSessionConfigBuilder(mSessionConfigBuilder,
+                        streamInfo,
+                        attachedStreamSpec);
                 updateSessionConfig(mSessionConfigBuilder.build());
                 notifyReset();
             } else if (currentStreamInfo.getStreamState() != streamInfo.getStreamState()) {
-                applyStreamInfoToSessionConfigBuilder(mSessionConfigBuilder, streamInfo);
+                applyStreamInfoAndStreamSpecToSessionConfigBuilder(mSessionConfigBuilder,
+                        streamInfo,
+                        attachedStreamSpec);
                 updateSessionConfig(mSessionConfigBuilder.build());
                 notifyUpdated();
             }
@@ -892,8 +915,9 @@
 
     @MainThread
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    void applyStreamInfoToSessionConfigBuilder(@NonNull SessionConfig.Builder sessionConfigBuilder,
-            @NonNull StreamInfo streamInfo) {
+    void applyStreamInfoAndStreamSpecToSessionConfigBuilder(
+            @NonNull SessionConfig.Builder sessionConfigBuilder,
+            @NonNull StreamInfo streamInfo, @NonNull StreamSpec streamSpec) {
         final boolean isStreamError = streamInfo.getId() == StreamInfo.STREAM_ID_ERROR;
         final boolean isStreamActive = streamInfo.getStreamState() == StreamState.ACTIVE;
         if (isStreamError && isStreamActive) {
@@ -902,11 +926,12 @@
         }
 
         sessionConfigBuilder.clearSurfaces();
+        DynamicRange dynamicRange = streamSpec.getDynamicRange();
         if (!isStreamError) {
             if (isStreamActive) {
-                sessionConfigBuilder.addSurface(mDeferrableSurface);
+                sessionConfigBuilder.addSurface(mDeferrableSurface, dynamicRange);
             } else {
-                sessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface);
+                sessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface, dynamicRange);
             }
         } // Don't attach surface when stream is invalid.
 
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java
index 557a2a7..735a24e 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java
@@ -24,6 +24,7 @@
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
 import android.os.Build;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -31,8 +32,10 @@
 import androidx.camera.core.impl.Quirk;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 /**
  * <p>QuirkSummary
@@ -126,6 +129,18 @@
         return false;
     }
 
+    /**
+     * Returns the extra supported sizes for the problematic devices if any, otherwise returns an
+     * empty set.
+     */
+    @NonNull
+    public static Set<Size> getExtraSupportedSizes() {
+        if (isFHDProblematicDevice()) {
+            return Collections.singleton(new Size(1920, 1080));
+        }
+        return Collections.emptySet();
+    }
+
     private static boolean isFHDProblematicDevice() {
         return INCORRECT_FHD_PROFILE_MODEL_LIST.contains(Build.MODEL.toLowerCase(Locale.US));
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapper.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapper.java
index 5b5778b..9f3448a 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapper.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapper.java
@@ -29,6 +29,9 @@
 import androidx.camera.video.internal.encoder.VideoEncoderInfo;
 import androidx.core.util.Preconditions;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Workaround to wrap the VideoEncoderInfo in order to fix the wrong information provided by
  * {@link MediaCodecInfo}.
@@ -48,6 +51,10 @@
     private final VideoEncoderInfo mVideoEncoderInfo;
     private final Range<Integer> mSupportedWidths;
     private final Range<Integer> mSupportedHeights;
+    // Extra supported sizes is used to put resolutions that are actually supported on the device
+    // but the MediaCodecInfo indicates the resolution is invalid. The most common one is
+    // 1920x1080. For resolutions in this set, #isSizeSupported(w, h) should return true.
+    private final Set<Size> mExtraSupportedSizes = new HashSet<>();
 
     /**
      * Check and wrap an input VideoEncoderInfo
@@ -89,10 +96,11 @@
         } else {
             toWrap = false;
         }
-        return toWrap ? new VideoEncoderInfoWrapper(videoEncoderInfo) : videoEncoderInfo;
+        return toWrap ? new VideoEncoderInfoWrapper(videoEncoderInfo, validSizeToCheck)
+                : videoEncoderInfo;
     }
 
-    VideoEncoderInfoWrapper(@NonNull VideoEncoderInfo videoEncoderInfo) {
+    VideoEncoderInfoWrapper(@NonNull VideoEncoderInfo videoEncoderInfo, @Nullable Size validSize) {
         mVideoEncoderInfo = videoEncoderInfo;
 
         // Ideally we should find out supported widths/heights for each problematic device.
@@ -104,6 +112,12 @@
         int heightAlignment = videoEncoderInfo.getHeightAlignment();
         int maxHeight = (int) Math.ceil((double) HEIGHT_4KDCI / heightAlignment) * heightAlignment;
         mSupportedHeights = Range.create(heightAlignment, maxHeight);
+
+        if (validSize != null) {
+            mExtraSupportedSizes.add(validSize);
+        }
+        mExtraSupportedSizes.addAll(
+                MediaCodecInfoReportIncorrectInfoQuirk.getExtraSupportedSizes());
     }
 
     @NonNull
@@ -114,6 +128,10 @@
 
     @Override
     public boolean isSizeSupported(int width, int height) {
+        if (!mExtraSupportedSizes.isEmpty() && mExtraSupportedSizes.contains(
+                new Size(width, height))) {
+            return true;
+        }
         return mSupportedWidths.contains(width)
                 && mSupportedHeights.contains(height)
                 && width % mVideoEncoderInfo.getWidthAlignment() == 0
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 02ef374..ccc85b0 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
@@ -57,6 +57,7 @@
 import androidx.camera.core.impl.EncoderProfilesProxy
 import androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
 import androidx.camera.core.impl.ImageOutputConfig
+import androidx.camera.core.impl.MutableOptionsBundle
 import androidx.camera.core.impl.MutableStateObservable
 import androidx.camera.core.impl.Observable
 import androidx.camera.core.impl.StreamSpec
@@ -141,6 +142,12 @@
     private lateinit var camera: FakeCamera
     private var surfaceRequestsToRelease = mutableListOf<SurfaceRequest>()
     private val handlersToRelease = mutableListOf<Handler>()
+    private val testImplementationOption: androidx.camera.core.impl.Config.Option<Int> =
+        androidx.camera.core.impl.Config.Option.create(
+            "test.testOption",
+            Int::class.javaPrimitiveType!!
+        )
+    private val testImplementationOptionValue = 5
 
     @Before
     fun setup() {
@@ -1276,6 +1283,28 @@
         )
     }
 
+    @Test
+    fun sessionConfigHasStreamSpecImplementationOptions_whenUpdateStreamSpecImplOptions() {
+        // Arrange.
+        setupCamera()
+        createCameraUseCaseAdapter()
+        cameraUseCaseAdapter.setEffects(listOf(createFakeEffect()))
+        val videoCapture = createVideoCapture(createVideoOutput())
+        // Act.
+        addAndAttachUseCases(videoCapture)
+        val newImplementationOptionValue = 6
+        val streamSpecOptions = MutableOptionsBundle.create()
+        streamSpecOptions.insertOption(testImplementationOption, newImplementationOptionValue)
+        videoCapture.updateSuggestedStreamSpecImplementationOptions(streamSpecOptions)
+        addAndAttachUseCases(videoCapture)
+        // Assert.
+        assertThat(
+            videoCapture.sessionConfig.implementationOptions.retrieveOption(
+                testImplementationOption
+            )
+        ).isEqualTo(newImplementationOptionValue)
+    }
+
     private fun testSurfaceRequestContainsExpected(
         quality: Quality = HD, // HD maps to 1280x720 (4:3)
         videoEncoderInfo: VideoEncoderInfo = createVideoEncoderInfo(),
@@ -1347,10 +1376,10 @@
         supportedHeights: Range<Int> = Range.create(1, Integer.MAX_VALUE),
     ): VideoEncoderInfo {
         return FakeVideoEncoderInfo(
-            _widthAlignment = widthAlignment,
-            _heightAlignment = heightAlignment,
-            _supportedWidths = supportedWidths,
-            _supportedHeights = supportedHeights,
+            widthAlignment = widthAlignment,
+            heightAlignment = heightAlignment,
+            supportedWidths = supportedWidths,
+            supportedHeights = supportedHeights,
         )
     }
 
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapperTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapperTest.kt
index e03e90e..aa15d2a 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapperTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapperTest.kt
@@ -43,7 +43,7 @@
     private val SUPPORTED_WIDTHS = Range.create(WIDTH_ALIGNMENT, 640)
     private val SUPPORTED_HEIGHTS = Range.create(HEIGHT_ALIGNMENT, 480)
     private val VALID_SIZE = Size(320, 240)
-    private val INVALID_SIZE = Size(1920, 1080)
+    private val SIZE_SHOULD_BE_VALID = Size(1920, 1080)
 
     private const val WIDTH_4KDCI = 4096
     private const val HEIGHT_4KDCI = 2160
@@ -52,10 +52,10 @@
 
     private val baseVideoEncoderInfo by lazy {
         FakeVideoEncoderInfo(
-            _supportedWidths = SUPPORTED_WIDTHS,
-            _supportedHeights = SUPPORTED_HEIGHTS,
-            _widthAlignment = WIDTH_ALIGNMENT,
-            _heightAlignment = HEIGHT_ALIGNMENT,
+            supportedWidths = SUPPORTED_WIDTHS,
+            supportedHeights = SUPPORTED_HEIGHTS,
+            widthAlignment = WIDTH_ALIGNMENT,
+            heightAlignment = HEIGHT_ALIGNMENT,
         )
     }
 
@@ -86,7 +86,7 @@
                 arrayOf(
                     NONE_QUIRK_BRAND,
                     NONE_QUIRK_MODEL,
-                    INVALID_SIZE,
+                    SIZE_SHOULD_BE_VALID,
                     true,
                 ),
                 arrayOf(
@@ -125,41 +125,37 @@
     @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
     class WrappingMethodTest {
 
-        private lateinit var videoEncoderInfo: VideoEncoderInfo
-
-        @Before
-        fun setup() {
-            // Set an invalid size to enable wrapping
-            videoEncoderInfo = VideoEncoderInfoWrapper.from(baseVideoEncoderInfo, INVALID_SIZE)
-            assertThat(videoEncoderInfo is VideoEncoderInfoWrapper).isTrue()
-        }
-
         @Test
         fun willNotWrapAWrapper() {
-            val videoEncoderInfo2 = VideoEncoderInfoWrapper.from(videoEncoderInfo, INVALID_SIZE)
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
+            val videoEncoderInfo2 = createFakeVideoEncoderInfoWrapper(videoEncoderInfo)
             assertThat(videoEncoderInfo2).isSameInstanceAs(videoEncoderInfo)
         }
 
         @Test
         fun getSupportedWidths() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThat(videoEncoderInfo.supportedWidths).isEqualTo(OVERRIDDEN_SUPPORTED_WIDTHS)
             assertThat(videoEncoderInfo.supportedHeights).isEqualTo(OVERRIDDEN_SUPPORTED_HEIGHTS)
         }
 
         @Test
         fun getSupportedHeight() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThat(videoEncoderInfo.supportedWidths).isEqualTo(OVERRIDDEN_SUPPORTED_WIDTHS)
             assertThat(videoEncoderInfo.supportedHeights).isEqualTo(OVERRIDDEN_SUPPORTED_HEIGHTS)
         }
 
         @Test
         fun getSupportedHeightsFor() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThat(videoEncoderInfo.getSupportedHeightsFor(640))
                 .isEqualTo(OVERRIDDEN_SUPPORTED_HEIGHTS)
         }
 
         @Test
         fun getSupportedHeightFor_invalidWidth() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThrows(IllegalArgumentException::class.java) {
                 // Too large
                 assertThat(videoEncoderInfo.getSupportedHeightsFor(5000))
@@ -172,12 +168,14 @@
 
         @Test
         fun getSupportedWidthsFor() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThat(videoEncoderInfo.getSupportedWidthsFor(480))
                 .isEqualTo(OVERRIDDEN_SUPPORTED_WIDTHS)
         }
 
         @Test
         fun getSupportedWidthFor_invalidHeight() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThrows(IllegalArgumentException::class.java) {
                 // Too large
                 assertThat(videoEncoderInfo.getSupportedWidthsFor(5000))
@@ -190,15 +188,62 @@
 
         @Test
         fun isSizeSupported() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             assertThat(videoEncoderInfo.isSizeSupported(640, 480)).isTrue()
         }
 
         @Test
         fun isSizeSupported_invalidSize() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper()
             // Too large
             assertThat(videoEncoderInfo.isSizeSupported(5000, 5000)).isFalse()
             // Non alignment
             assertThat(videoEncoderInfo.isSizeSupported(333, 333)).isFalse()
         }
+
+        @Test
+        fun isSizeSupported_validSizeIsSupported() {
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper(
+                createFakeVideoEncoderInfo(
+                    supportedWidths = Range(16, 640),
+                    supportedHeights = Range(16, 480),
+                    widthAlignment = 16,
+                    heightAlignment = 16,
+                ), validSizeToCheck = Size(1920, 1080) // 1080 not align to 16
+            )
+            assertThat(videoEncoderInfo.isSizeSupported(1920, 1080)).isTrue()
+        }
+
+        @Test
+        fun isSizeSupported_supportFhdForFhdProblematicDevices() {
+            ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "sm-a032f")
+
+            val videoEncoderInfo = createFakeVideoEncoderInfoWrapper(
+                createFakeVideoEncoderInfo(
+                    supportedWidths = Range(16, 640),
+                    supportedHeights = Range(16, 480),
+                    widthAlignment = 16,
+                    heightAlignment = 16,
+                ), null
+            )
+            assertThat(videoEncoderInfo.isSizeSupported(1920, 1080)).isTrue()
+        }
+
+        private fun createFakeVideoEncoderInfoWrapper(
+            videoEncoderInfo: VideoEncoderInfo = createFakeVideoEncoderInfo(),
+            validSizeToCheck: Size? = SIZE_SHOULD_BE_VALID,
+        ) = VideoEncoderInfoWrapper.from(videoEncoderInfo, validSizeToCheck)
+
+        private fun createFakeVideoEncoderInfo(
+            supportedWidths: Range<Int> = SUPPORTED_WIDTHS,
+            supportedHeights: Range<Int> = SUPPORTED_HEIGHTS,
+            widthAlignment: Int = WIDTH_ALIGNMENT,
+            heightAlignment: Int = HEIGHT_ALIGNMENT,
+        ) = FakeVideoEncoderInfo(
+            supportedWidths = supportedWidths,
+            supportedHeights = supportedHeights,
+            widthAlignment = widthAlignment,
+            heightAlignment = heightAlignment,
+        )
     }
 }
diff --git a/camera/camera-view/src/main/AndroidManifest.xml b/camera/camera-view/src/main/AndroidManifest.xml
deleted file mode 100644
index 39dd6cb..0000000
--- a/camera/camera-view/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<manifest />
diff --git a/camera/camera-viewfinder-compose/api/current.txt b/camera/camera-viewfinder-compose/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder-compose/api/public_plus_experimental_current.txt b/camera/camera-viewfinder-compose/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/public_plus_experimental_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder-compose/api/res-current.txt b/camera/camera-viewfinder-compose/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/res-current.txt
diff --git a/camera/camera-viewfinder-compose/api/restricted_current.txt b/camera/camera-viewfinder-compose/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder-compose/build.gradle b/camera/camera-viewfinder-compose/build.gradle
new file mode 100644
index 0000000..3476e5e
--- /dev/null
+++ b/camera/camera-viewfinder-compose/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    // Add dependencies here
+}
+
+android {
+    namespace "androidx.camera.viewfinder.compose"
+}
+
+androidx {
+    name = "androidx.camera:camera-viewfinder-compose"
+    type = LibraryType.PUBLISHED_LIBRARY
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    inceptionYear = "2023"
+    description = "Composable ViewFinder implementation for CameraX"
+}
diff --git a/camera/camera-viewfinder-compose/src/main/java/androidx/camera/androidx-camera-camera-viewfinder-compose-documentation.md b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/androidx-camera-camera-viewfinder-compose-documentation.md
new file mode 100644
index 0000000..10d4659
--- /dev/null
+++ b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/androidx-camera-camera-viewfinder-compose-documentation.md
@@ -0,0 +1,7 @@
+# Module root
+
+CameraX ViewFinder Compose
+
+# Package androidx.camera.viewfinder.compose
+
+Library providing a composable ViewFinder
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt
index 2fb1a60..5efe358 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt
@@ -19,7 +19,6 @@
 import android.Manifest
 import android.content.Context
 import android.content.Intent
-import android.os.Build
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.lifecycle.ProcessCameraProvider
@@ -39,7 +38,6 @@
 import java.util.concurrent.TimeUnit
 import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.After
-import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Rule
@@ -96,11 +94,6 @@
     @Before
     fun setUp() {
         assumeTrue(CameraUtil.deviceHasCamera())
-        assumeFalse(
-            "See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-                " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30
-        )
         CoreAppTestUtil.assumeCompatibleDevice()
         // Use the natural orientation throughout these tests to ensure the activity isn't
         // recreated unexpectedly. This will also freeze the sensors until
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
index 912d748..0c9a82a 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
@@ -100,9 +100,6 @@
             "Ignore Cuttlefish",
             Build.MODEL.contains("Cuttlefish")
         )
-        Assume.assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-            " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
         Assume.assumeTrue(CameraUtil.deviceHasCamera())
         CoreAppTestUtil.assumeCompatibleDevice()
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index 5402bfe..1208c6a 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -68,6 +68,7 @@
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.CoreAppTestUtil
 import androidx.camera.testing.SurfaceTextureProvider
 import androidx.camera.testing.fakes.FakeLifecycleOwner
 import androidx.camera.testing.fakes.FakeSessionProcessor
@@ -98,7 +99,6 @@
 import org.junit.Assume.assumeNotNull
 import org.junit.Assume.assumeTrue
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
@@ -150,9 +150,7 @@
 
     @Before
     fun setUp(): Unit = runBlocking {
-        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-            " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
+        CoreAppTestUtil.assumeCompatibleDevice()
         assumeTrue(CameraUtil.hasCameraWithLensFacing(BACK_LENS_FACING))
         createDefaultPictureFolderIfNotExist()
         ProcessCameraProvider.configureInstance(cameraXConfig)
@@ -1580,8 +1578,13 @@
     }
 
     @Test
-    @Ignore("b/280379397")
     fun unbindVideoCaptureWithoutStartingRecorder_imageCapturingShouldSuccess() = runBlocking {
+        assumeTrue("b/280379397", implName != Camera2Config::class.simpleName)
+        assumeTrue(
+            "b/280560222: takePicture request is discarded if UseCaseCamera is recreated",
+            implName != CameraPipeConfig::class.simpleName
+        )
+
         // Arrange.
         val imageCapture = ImageCapture.Builder().build()
         val videoStreamReceived = CompletableDeferred<Boolean>()
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt
index b8d8429..f7078e5 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt
@@ -16,11 +16,10 @@
 
 package androidx.camera.integration.uiwidgets.rotations
 
-import android.os.Build
+import androidx.camera.testing.CoreAppTestUtil
 import androidx.test.core.app.ActivityScenario
 import androidx.test.filters.LargeTest
 import org.junit.After
-import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -56,11 +55,7 @@
 
     @Before
     fun before() {
-        Assume.assumeFalse(
-            "See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-                " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30
-        )
+        CoreAppTestUtil.assumeCompatibleDevice()
         setUp(lensFacing)
     }
 
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
index 1ff8e45..782127b 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import android.view.Surface
 import android.view.View
+import androidx.camera.testing.CoreAppTestUtil
 import androidx.test.core.app.ActivityScenario
 import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
@@ -76,10 +77,7 @@
                 "redmi note 8"
             ).contains(Build.MODEL.lowercase(Locale.US)) && rotation == Surface.ROTATION_180
         )
-        Assume.assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-            " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
-
+        CoreAppTestUtil.assumeCompatibleDevice()
         setUp(lensFacing)
     }
 
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
index aaedb05..94d4e55 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
@@ -17,16 +17,15 @@
 package androidx.camera.integration.uiwidgets.rotations
 
 import android.app.Instrumentation
-import android.os.Build
 import androidx.camera.core.CameraSelector
 import androidx.camera.integration.uiwidgets.rotations.CameraActivity.Companion.IMAGE_CAPTURE_MODE_FILE
 import androidx.camera.integration.uiwidgets.rotations.CameraActivity.Companion.IMAGE_CAPTURE_MODE_IN_MEMORY
 import androidx.camera.integration.uiwidgets.rotations.CameraActivity.Companion.IMAGE_CAPTURE_MODE_MEDIA_STORE
 import androidx.camera.integration.uiwidgets.rotations.CameraActivity.Companion.IMAGE_CAPTURE_MODE_OUTPUT_STREAM
+import androidx.camera.testing.CoreAppTestUtil
 import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
 import org.junit.After
-import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -89,9 +88,7 @@
 
     @Before
     fun before() {
-        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-            " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
+        CoreAppTestUtil.assumeCompatibleDevice()
         setUp(lensFacing)
     }
 
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index b7d5960..17631cd 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -115,9 +115,7 @@
 
     @Before
     fun setup() {
-        assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
-            " the test failure.",
-            Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
+        CoreAppTestUtil.assumeCompatibleDevice()
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window before start the test.
         CoreAppTestUtil.prepareDeviceUI(instrumentation)
diff --git a/cardview/cardview/src/main/AndroidManifest.xml b/cardview/cardview/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/cardview/cardview/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/collection/collection-benchmark/src/androidMain/AndroidManifest.xml b/collection/collection-benchmark/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/collection/collection-benchmark/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/collection/collection/api/current.ignore b/collection/collection/api/current.ignore
index 7be6378..3ee4c0b 100644
--- a/collection/collection/api/current.ignore
+++ b/collection/collection/api/current.ignore
@@ -1,4 +1,8 @@
 // Baseline format: 1.0
+ChangedAbstract: androidx.collection.ArraySet#size():
+    Method androidx.collection.ArraySet.size has changed 'abstract' qualifier
+
+
 ChangedType: androidx.collection.ArraySet#iterator():
     Method androidx.collection.ArraySet.iterator has changed return type from java.util.Iterator<E!> to java.util.Iterator<E>
 ChangedType: androidx.collection.LongSparseArray#clone():
@@ -25,9 +29,5 @@
     Removed constructor androidx.collection.ArraySet(androidx.collection.ArraySet<E>)
 RemovedMethod: androidx.collection.ArraySet#ArraySet(java.util.Collection<E>):
     Removed constructor androidx.collection.ArraySet(java.util.Collection<E>)
-RemovedMethod: androidx.collection.ArraySet#size():
-    Removed method androidx.collection.ArraySet.size()
-RemovedMethod: androidx.collection.LruCache#toString():
-    Removed method androidx.collection.LruCache.toString()
 RemovedMethod: androidx.collection.SimpleArrayMap#SimpleArrayMap(androidx.collection.SimpleArrayMap<K,V>):
     Removed constructor androidx.collection.SimpleArrayMap(androidx.collection.SimpleArrayMap<K,V>)
diff --git a/collection/collection/api/restricted_current.ignore b/collection/collection/api/restricted_current.ignore
index 7be6378..3ee4c0b 100644
--- a/collection/collection/api/restricted_current.ignore
+++ b/collection/collection/api/restricted_current.ignore
@@ -1,4 +1,8 @@
 // Baseline format: 1.0
+ChangedAbstract: androidx.collection.ArraySet#size():
+    Method androidx.collection.ArraySet.size has changed 'abstract' qualifier
+
+
 ChangedType: androidx.collection.ArraySet#iterator():
     Method androidx.collection.ArraySet.iterator has changed return type from java.util.Iterator<E!> to java.util.Iterator<E>
 ChangedType: androidx.collection.LongSparseArray#clone():
@@ -25,9 +29,5 @@
     Removed constructor androidx.collection.ArraySet(androidx.collection.ArraySet<E>)
 RemovedMethod: androidx.collection.ArraySet#ArraySet(java.util.Collection<E>):
     Removed constructor androidx.collection.ArraySet(java.util.Collection<E>)
-RemovedMethod: androidx.collection.ArraySet#size():
-    Removed method androidx.collection.ArraySet.size()
-RemovedMethod: androidx.collection.LruCache#toString():
-    Removed method androidx.collection.LruCache.toString()
 RemovedMethod: androidx.collection.SimpleArrayMap#SimpleArrayMap(androidx.collection.SimpleArrayMap<K,V>):
     Removed constructor androidx.collection.SimpleArrayMap(androidx.collection.SimpleArrayMap<K,V>)
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 2482074..4777d20 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -15,9 +15,8 @@
  */
 
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -25,65 +24,15 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
-    if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-        api("androidx.annotation:annotation:1.1.0")
-
-        implementation("androidx.compose.runtime:runtime:1.2.1")
-        implementation("androidx.compose.ui:ui:1.2.1")
-        implementation("androidx.compose.ui:ui-unit:1.2.1")
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-        implementation(libs.kotlinStdlib)
-        api(libs.kotlinCoroutinesCore)
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-        testImplementation(libs.kotlinCoroutinesCore)
-
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testCore)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(project(":compose:animation:animation"))
-        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.2.1")
-        androidTestImplementation(project(":compose:test-utils"))
-
-        lintPublish project(":compose:animation:animation-core-lint")
-
-        samples(project(":compose:animation:animation-core:animation-core-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 {
-
-            jvmMain {
-                dependencies {
-                    implementation(libs.kotlinStdlib)
-                    api(libs.kotlinCoroutinesCore)
-                }
-            }
-
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui"))
                 implementation(project(":compose:ui:ui-unit"))
@@ -91,45 +40,86 @@
                 implementation(libs.kotlinStdlibCommon)
                 api(libs.kotlinCoroutinesCore)
             }
+        }
 
-            androidMain {
-                dependencies {
-                    api("androidx.annotation:annotation:1.1.0")
-                    implementation(libs.kotlinStdlib)
-                }
+        commonTest {
+            dependencies {
             }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+                implementation(libs.kotlinStdlib)
+                api(libs.kotlinCoroutinesCore)
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api("androidx.annotation:annotation:1.1.0")
+                implementation(libs.kotlinStdlib)
+            }
+        }
+
+        if (desktopEnabled) {
             desktopMain {
+                dependsOn(jvmMain)
                 dependencies {
                     implementation(libs.kotlinStdlib)
+                    implementation(project(":compose:runtime:runtime"))
+                    implementation(project(":compose:ui:ui"))
+                    implementation(project(":compose:ui:ui-unit"))
+                    implementation(project(":compose:ui:ui-util"))
                 }
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
+        jvmTest {
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.testCore)
+                implementation(libs.junit)
+                implementation(project(":compose:animation:animation"))
+                implementation("androidx.compose.ui:ui-test-junit4:1.2.1")
+                implementation(project(":compose:test-utils"))
+            }
+        }
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.junit)
                 implementation(libs.truth)
                 implementation(libs.kotlinCoroutinesCore)
             }
+        }
 
-            androidAndroidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.testCore)
-                implementation(libs.junit)
-                implementation(project(":compose:animation:animation"))
-                implementation(project(":compose:ui:ui-test-junit4"))
-                implementation(project(":compose:test-utils"))
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
             }
         }
     }
-    dependencies {
-        samples(project(":compose:animation:animation-core:animation-core-samples"))
-    }
+}
+
+dependencies {
+    lintPublish project(":compose:animation:animation-core-lint")
 }
 
 androidx {
diff --git a/compose/animation/animation-core/samples/src/main/AndroidManifest.xml b/compose/animation/animation-core/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index dc2f180..0000000
--- a/compose/animation/animation-core/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/animation/animation-core/src/androidAndroidTest/AndroidManifest.xml b/compose/animation/animation-core/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 127ad73..0000000
--- a/compose/animation/animation-core/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/animation/animation-core/src/androidMain/AndroidManifest.xml b/compose/animation/animation-core/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 34d825b..0000000
--- a/compose/animation/animation-core/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 21c13c8..ccbfd2a 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -39,6 +39,7 @@
 import androidx.compose.ui.unit.IntSize
 import kotlin.math.max
 import androidx.compose.animation.core.internal.JvmDefaultWithCompatibility
+import androidx.compose.runtime.mutableLongStateOf
 import kotlin.jvm.JvmName
 
 /**
@@ -231,8 +232,8 @@
      * @suppress
      */
     @InternalAnimationApi
-    var playTimeNanos by mutableStateOf(0L)
-    private var startTimeNanos by mutableStateOf(AnimationConstants.UnspecifiedTime)
+    var playTimeNanos by mutableLongStateOf(0L)
+    private var startTimeNanos by mutableLongStateOf(AnimationConstants.UnspecifiedTime)
 
     // This gets calculated every time child is updated/added
     internal var updateChildrenNeeded: Boolean by mutableStateOf(true)
@@ -495,7 +496,7 @@
             private set
 
         internal var isFinished: Boolean by mutableStateOf(true)
-        private var offsetTimeNanos by mutableStateOf(0L)
+        private var offsetTimeNanos by mutableLongStateOf(0L)
         private var needsReset by mutableStateOf(false)
 
         // Changed during animation, no concerns of rolling back
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index c6597a7..bb4c4c1 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -15,7 +15,7 @@
  */
 
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
@@ -25,55 +25,15 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-        api("androidx.annotation:annotation:1.1.0")
-        api(project(":compose:animation:animation"))
-        api("androidx.compose.foundation:foundation-layout:1.2.1")
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api("androidx.compose.ui:ui:1.2.1")
-        api("androidx.compose.ui:ui-geometry:1.2.1")
-
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-        implementation(libs.kotlinStdlibCommon)
-        implementation("androidx.core:core-ktx:1.5.0")
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-
-        androidTestImplementation("androidx.compose.foundation:foundation:1.2.1")
-        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.2.1")
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-
-        samples(project(":compose:animation:animation-graphics:animation-graphics-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
 
                 api(project(":compose:animation:animation"))
@@ -84,39 +44,84 @@
 
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
+        androidMain.dependencies {
+            api("androidx.annotation:annotation:1.1.0")
+            implementation("androidx.core:core-ktx:1.5.0")
+        }
 
-            androidMain.dependencies {
-                api("androidx.annotation:annotation:1.1.0")
-                implementation("androidx.core:core-ktx:1.5.0")
+        commonTest {
+            dependencies {
             }
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
             }
+        }
 
-            androidAndroidTest.dependencies {
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+                    api(project(":compose:foundation:foundation-layout"))
+                    api(project(":compose:runtime:runtime"))
+                    api(project(":compose:ui:ui"))
+                    api(project(":compose:ui:ui-geometry"))
+                    implementation(project(":compose:ui:ui-util"))
+                }
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.junit)
                 implementation(libs.truth)
-                implementation(project(":compose:foundation:foundation"))
-                implementation(project(":compose:ui:ui-test-junit4"))
+                implementation("androidx.compose.foundation:foundation:1.2.1")
+                implementation("androidx.compose.ui:ui-test-junit4:1.2.1")
                 implementation(project(":compose:test-utils"))
             }
         }
-    }
-    dependencies {
-        samples(project(":compose:animation:animation-graphics:animation-graphics-samples"))
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                }
+            }
+        }
     }
 }
 
diff --git a/compose/animation/animation-graphics/samples/src/main/AndroidManifest.xml b/compose/animation/animation-graphics/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 3063116..0000000
--- a/compose/animation/animation-graphics/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
--->
-<manifest />
diff --git a/compose/animation/animation-graphics/src/androidAndroidTest/AndroidManifest.xml b/compose/animation/animation-graphics/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/compose/animation/animation-graphics/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/animation/animation-graphics/src/androidMain/AndroidManifest.xml b/compose/animation/animation-graphics/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 3063116..0000000
--- a/compose/animation/animation-graphics/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
--->
-<manifest />
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index 8d6b39f..f2b4f811 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -25,56 +23,15 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-        api("androidx.annotation:annotation:1.1.0")
-        api(project(":compose:animation:animation-core"))
-        api("androidx.compose.foundation:foundation-layout:1.2.1")
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api("androidx.compose.ui:ui:1.2.1")
-        api("androidx.compose.ui:ui-geometry:1.2.1")
-
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-        implementation(libs.kotlinStdlibCommon)
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-
-        androidTestImplementation("androidx.compose.foundation:foundation:1.2.1")
-        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.2.1")
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-
-        lintPublish project(":compose:animation:animation-lint")
-
-        samples(project(":compose:animation:animation:animation-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
 
                 api(project(":compose:animation:animation-core"))
@@ -85,39 +42,84 @@
 
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            androidMain.dependencies {
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.annotation:annotation:1.1.0")
             }
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+
+                    api(project(":compose:foundation:foundation-layout"))
+                    api(project(":compose:runtime:runtime"))
+                    api(project(":compose:ui:ui"))
+                    api(project(":compose:ui:ui-geometry"))
+
+                    implementation(project(":compose:ui:ui-util"))
+                }
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
+        jvmTest {
+            dependencies {
             }
+        }
 
-            androidAndroidTest.dependencies {
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.junit)
                 implementation(libs.truth)
-                implementation(project(":compose:foundation:foundation"))
-                implementation(project(":compose:ui:ui-test-junit4"))
+                implementation("androidx.compose.foundation:foundation:1.2.1")
+                implementation("androidx.compose.ui:ui-test-junit4:1.2.1")
                 implementation(project(":compose:test-utils"))
             }
         }
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+            }
+        }
     }
-    dependencies {
-        samples(project(":compose:animation:animation:animation-samples"))
-    }
+}
+
+dependencies {
+    lintPublish project(":compose:animation:animation-lint")
 }
 
 androidx {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/AndroidManifest.xml b/compose/animation/animation/integration-tests/animation-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index dc2f180..0000000
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/AnimatedClockDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/AnimatedClockDemo.kt
index f7b4e9ca..e3a7df2 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/AnimatedClockDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/AnimatedClockDemo.kt
@@ -38,7 +38,8 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -63,9 +64,9 @@
 @Composable
 fun AnimatedClockDemo() {
     val calendar = remember { Calendar.getInstance() }
-    val seconds = remember { mutableStateOf(calendar[Calendar.SECOND]) }
-    val minutes = remember { mutableStateOf(calendar[Calendar.MINUTE]) }
-    val hours = remember { mutableStateOf(calendar[Calendar.HOUR_OF_DAY]) }
+    val seconds = remember { mutableIntStateOf(calendar[Calendar.SECOND]) }
+    val minutes = remember { mutableIntStateOf(calendar[Calendar.MINUTE]) }
+    val hours = remember { mutableIntStateOf(calendar[Calendar.HOUR_OF_DAY]) }
     LaunchedEffect(key1 = Unit) {
         // Start from 23:59:50 to give an impressive animation for all numbers
         calendar.set(2020, 10, 10, 23, 59, 50)
@@ -74,9 +75,9 @@
         while (isActive) {
             withInfiniteAnimationFrameMillis {
                 calendar.timeInMillis = it - firstFrameTime + initialTime
-                seconds.value = calendar[Calendar.SECOND]
-                minutes.value = calendar[Calendar.MINUTE]
-                hours.value = calendar[Calendar.HOUR_OF_DAY]
+                seconds.intValue = calendar[Calendar.SECOND]
+                minutes.intValue = calendar[Calendar.MINUTE]
+                hours.intValue = calendar[Calendar.HOUR_OF_DAY]
             }
         }
     }
@@ -110,7 +111,7 @@
         targetValue = ((9 - digit) * digitHeight).dp,
         animationSpec = tween(moveDuration),
     ).value
-    var circleOffset by remember { mutableStateOf(0f) }
+    var circleOffset by remember { mutableFloatStateOf(0f) }
     LaunchedEffect(digit) {
         if (digit == 0) return@LaunchedEffect // Don't animate for 0 as direction is reversed
         animate(
@@ -124,7 +125,7 @@
             animationSpec = spring(dampingRatio = 0.6f)
         ) { animationValue, _ -> circleOffset = animationValue }
     }
-    var circleStretch by remember { mutableStateOf(1f) }
+    var circleStretch by remember { mutableFloatStateOf(1f) }
     LaunchedEffect(digit) {
         if (digit == 0) return@LaunchedEffect // Don't animate for 0 as direction is reversed
         animate(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/ChatScreenDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/ChatScreenDemo.kt
index 565070f..8fe01ca 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/ChatScreenDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/fancy/ChatScreenDemo.kt
@@ -36,8 +36,8 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -113,13 +113,13 @@
 
 @Composable
 private fun animateCorner(hasSharpCorner: Boolean): MutableState<Float> {
-    val state = remember { mutableStateOf(roundCornerSize) }
+    val state = remember { mutableFloatStateOf(roundCornerSize) }
     LaunchedEffect(hasSharpCorner) {
         animate(
-            initialValue = state.value,
+            initialValue = state.floatValue,
             targetValue = if (hasSharpCorner) 2f else roundCornerSize,
             animationSpec = spring(stiffness = 50f, dampingRatio = 0.6f)
-        ) { animationValue, _ -> state.value = animationValue }
+        ) { animationValue, _ -> state.floatValue = animationValue }
     }
     return state
 }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt
index c1d1e00..dadf80c 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt
@@ -31,7 +31,7 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
@@ -57,7 +57,7 @@
             modifier = Modifier.padding(40.dp)
         )
         val animScroll = remember { Animatable(0f) }
-        val itemWidth = remember { mutableStateOf(0f) }
+        val itemWidth = remember { mutableFloatStateOf(0f) }
         val scope = rememberCoroutineScope()
         val modifier = Modifier.draggable(
             orientation = Orientation.Horizontal,
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SpringBackScrollingDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SpringBackScrollingDemo.kt
index b954194..71d4605 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SpringBackScrollingDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SpringBackScrollingDemo.kt
@@ -36,6 +36,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -64,8 +65,8 @@
             fontSize = 20.sp
         )
 
-        var scrollPosition by remember { mutableStateOf(0f) }
-        val itemWidth = remember { mutableStateOf(0f) }
+        var scrollPosition by remember { mutableFloatStateOf(0f) }
+        var itemWidth by remember { mutableFloatStateOf(0f) }
         val mutatorMutex = remember { MutatorMutex() }
         var animation by remember { mutableStateOf(AnimationState(scrollPosition)) }
 
@@ -96,7 +97,7 @@
                             val springBackTarget: Float = calculateSpringBackTarget(
                                 target,
                                 latestVelocityX,
-                                itemWidth.value
+                                itemWidth
                             )
 
                             animation.animateDecay(exponentialDecay()) {
@@ -129,7 +130,7 @@
             }
         }
         Canvas(gesture.fillMaxWidth().height(400.dp)) {
-            itemWidth.value = size.width / 2f
+            itemWidth = size.width / 2f
             if (DEBUG) {
                 println(
                     "Anim, Spring back scrolling, redrawing with new" +
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt
index befd745..faa1d47 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt
@@ -34,7 +34,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -57,7 +57,7 @@
 @Composable
 fun SwipeToDismissDemo() {
     Column {
-        var index by remember { mutableStateOf(0) }
+        var index by remember { mutableIntStateOf(0) }
         Box(Modifier.requiredHeight(300.dp).fillMaxWidth()) {
             Box(
                 Modifier.swipeToDismiss(index).align(Alignment.BottomCenter).requiredSize(150.dp)
@@ -82,7 +82,7 @@
 
 private fun Modifier.swipeToDismiss(index: Int): Modifier = composed {
     val animatedOffset = remember { Animatable(0f) }
-    val height = remember { mutableStateOf(0) }
+    val height = remember { mutableIntStateOf(0) }
     LaunchedEffect(index) {
         animatedOffset.snapTo(0f)
     }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedContentWithContentKeyDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedContentWithContentKeyDemo.kt
index b8edc12..88ec8f8 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedContentWithContentKeyDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedContentWithContentKeyDemo.kt
@@ -31,6 +31,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -58,7 +59,7 @@
         ) {
             if (it.type == MyScreen.Type.Count) {
                 holder.SaveableStateProvider(it.type) {
-                    var count by rememberSaveable { mutableStateOf(0) }
+                    var count by rememberSaveable { mutableIntStateOf(0) }
                     Column(
                         Modifier.fillMaxSize(),
                         Arrangement.Center,
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedVisibilityDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedVisibilityDemo.kt
index 46e8603..0f679fd 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedVisibilityDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedVisibilityDemo.kt
@@ -49,6 +49,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -73,7 +74,7 @@
 
 @Composable
 fun AnimatedItems(animateContentSize: Boolean) {
-    var itemNum by remember { mutableStateOf(0) }
+    var itemNum by remember { mutableIntStateOf(0) }
     Column {
         Row(
             Modifier.fillMaxWidth().padding(20.dp),
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt
index 789d3f4..8e60214 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt
@@ -28,7 +28,8 @@
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -43,22 +44,22 @@
 @Composable
 fun AnimateBoundsModifierDemo() {
     var height by remember {
-        mutableStateOf(200)
+        mutableIntStateOf(200)
     }
     var left by remember {
-        mutableStateOf(0)
+        mutableIntStateOf(0)
     }
     var top by remember {
-        mutableStateOf(0)
+        mutableIntStateOf(0)
     }
     var right by remember {
-        mutableStateOf(0)
+        mutableIntStateOf(0)
     }
     var bottom by remember {
-        mutableStateOf(0)
+        mutableIntStateOf(0)
     }
     var weight by remember {
-        mutableStateOf(2f)
+        mutableFloatStateOf(2f)
     }
 
     LookaheadScope {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/CraneDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/CraneDemo.kt
index 4754294..dba31d2 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/CraneDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/CraneDemo.kt
@@ -39,6 +39,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.movableContentWithReceiverOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
@@ -172,7 +173,7 @@
 class ProgressProviderImpl<T>(initialState: T) : ProgressProvider<T> {
     override var initialState: T by mutableStateOf(initialState)
     override var targetState: T by mutableStateOf(initialState)
-    override var progress: Float by mutableStateOf(0f)
+    override var progress: Float by mutableFloatStateOf(0f)
 }
 
 interface ProgressProvider<T> {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt
index 4a0fad9..36d5ffd 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt
@@ -59,7 +59,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -98,7 +98,7 @@
                     if (hasPadding) Modifier.padding(bottom = 300.dp) else Modifier
                 )
         ) {
-            var state by remember { mutableStateOf(0) }
+            var state by remember { mutableIntStateOf(0) }
             val titles = listOf(
                 "SimpleScaffold", "W/Cutout", "SimpleSnackbar", "CustomSnackbar", "Backdrop"
             )
@@ -256,7 +256,7 @@
     Scaffold(
         scaffoldState = scaffoldState,
         floatingActionButton = {
-            var clickCount by remember { mutableStateOf(0) }
+            var clickCount by remember { mutableIntStateOf(0) }
             ExtendedFloatingActionButton(
                 text = { Text("Show snackbar") },
                 onClick = {
@@ -296,7 +296,7 @@
             }
         },
         floatingActionButton = {
-            var clickCount by remember { mutableStateOf(0) }
+            var clickCount by remember { mutableIntStateOf(0) }
             ExtendedFloatingActionButton(
                 text = { Text("Show snackbar") },
                 onClick = {
@@ -323,7 +323,7 @@
 @OptIn(ExperimentalMaterialApi::class)
 fun BackdropScaffoldSample() {
     val scope = rememberCoroutineScope()
-    val selection = remember { mutableStateOf(1) }
+    var selection by remember { mutableIntStateOf(1) }
     val scaffoldState = rememberBackdropScaffoldState(BackdropValue.Concealed)
     LaunchedEffect(scaffoldState) {
         scaffoldState.reveal()
@@ -345,7 +345,7 @@
                     }
                 },
                 actions = {
-                    var clickCount by remember { mutableStateOf(0) }
+                    var clickCount by remember { mutableIntStateOf(0) }
                     IconButton(
                         onClick = {
                             // show snackbar as a suspend function
@@ -364,10 +364,10 @@
         },
         backLayerContent = {
             LazyColumn {
-                items(if (selection.value >= 3) 3 else 5) {
+                items(if (selection >= 3) 3 else 5) {
                     ListItem(
                         Modifier.clickable {
-                            selection.value = it
+                            selection = it
                             scope.launch { scaffoldState.conceal() }
                         },
                         text = { Text("Select $it") }
@@ -376,7 +376,7 @@
             }
         },
         frontLayerContent = {
-            Text("Selection: ${selection.value}")
+            Text("Selection: $selection")
             LazyColumn {
                 items(50) {
                     ListItem(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt
index 6ec1f2b..b49e42f 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt
@@ -38,7 +38,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -79,7 +79,7 @@
 
 @Composable
 fun FancyTabs() {
-    var state by remember { mutableStateOf(0) }
+    var state by remember { mutableIntStateOf(0) }
     val titles = listOf("TAB 1", "TAB 2", "TAB 3")
     Column {
         TabRow(
@@ -124,7 +124,7 @@
 @OptIn(ExperimentalComposeUiApi::class)
 @Composable
 fun ScrollingTextTabs() {
-    var state by remember { mutableStateOf(0) }
+    var state by remember { mutableIntStateOf(0) }
     val titles = listOf(
         "Tab 1",
         "Tab 2",
@@ -159,7 +159,7 @@
 
 @Composable
 fun ScrollingFancyIndicatorContainerTabs() {
-    var state by remember { mutableStateOf(0) }
+    var state by remember { mutableIntStateOf(0) }
     val titles = listOf(
         "Tab 1",
         "Tab 2",
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/CrossfadeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/CrossfadeDemo.kt
index 1cf5b4a..2f77328 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/CrossfadeDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/CrossfadeDemo.kt
@@ -27,7 +27,7 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.saveable.rememberSaveableStateHolder
 import androidx.compose.runtime.setValue
@@ -41,7 +41,7 @@
 @Preview
 @Composable
 fun CrossfadeDemo() {
-    var current by rememberSaveable { mutableStateOf(0) }
+    var current by rememberSaveable { mutableIntStateOf(0) }
     Column {
         Row {
             tabs.forEachIndexed { index, tab ->
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/MultiDimensionalAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/MultiDimensionalAnimationDemo.kt
index c3b4274..a32e677 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/MultiDimensionalAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/MultiDimensionalAnimationDemo.kt
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -50,8 +51,8 @@
         }
     }
 
-    var width by remember { mutableStateOf(0f) }
-    var height by remember { mutableStateOf(0f) }
+    var width by remember { mutableFloatStateOf(0f) }
+    var height by remember { mutableFloatStateOf(0f) }
     val transition = updateTransition(currentState)
     val rect by transition.animateRect(transitionSpec = { spring(stiffness = 100f) }) {
         when (it) {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/InfiniteAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/InfiniteAnimationDemo.kt
index 9910030..b8e5bad 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/InfiniteAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/InfiniteAnimationDemo.kt
@@ -27,8 +27,10 @@
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -38,7 +40,7 @@
 @Preview
 @Composable
 fun InfiniteAnimationDemo() {
-    val alpha = remember { mutableStateOf(1f) }
+    var alpha by remember { mutableFloatStateOf(1f) }
     LaunchedEffect(Unit) {
         animate(
             initialValue = 1f,
@@ -48,7 +50,7 @@
                 repeatMode = RepeatMode.Reverse
             )
         ) { value, _ ->
-            alpha.value = value
+            alpha = value
         }
     }
     Box(Modifier.fillMaxSize()) {
@@ -59,7 +61,7 @@
                 .graphicsLayer(
                     scaleX = 3.0f,
                     scaleY = 3.0f,
-                    alpha = alpha.value
+                    alpha = alpha
                 ),
             tint = Color.Red
         )
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/SuspendDoubleTapToLikeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/SuspendDoubleTapToLikeDemo.kt
index 600de97..b3691cc 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/SuspendDoubleTapToLikeDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/SuspendDoubleTapToLikeDemo.kt
@@ -27,7 +27,7 @@
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
@@ -43,8 +43,8 @@
 @Preview
 @Composable
 fun SuspendDoubleTapToLikeDemo() {
-    var alpha by remember { mutableStateOf(0f) }
-    var scale by remember { mutableStateOf(0f) }
+    var alpha by remember { mutableFloatStateOf(0f) }
+    var scale by remember { mutableFloatStateOf(0f) }
 
     val mutatorMutex = MutatorMutex()
     val scope = rememberCoroutineScope()
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/AnimatedContentWithInterruptions.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/AnimatedContentWithInterruptions.kt
index 4e58285..48187047 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/AnimatedContentWithInterruptions.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/AnimatedContentWithInterruptions.kt
@@ -35,7 +35,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -47,7 +47,7 @@
 @OptIn(ExperimentalAnimationApi::class)
 @Composable
 fun AnimatedContentWithInterruptions() {
-    var count by remember { mutableStateOf(0) }
+    var count by remember { mutableIntStateOf(0) }
     AnimatedContent(targetState = count, transitionSpec = {
         if (targetState == 0) {
             (slideInVertically { it } togetherWith fadeOut(targetAlpha = 0.88f))
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/SeekingDebugging.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/SeekingDebugging.kt
index 6b38572..21756cd 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/SeekingDebugging.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/visualinspection/SeekingDebugging.kt
@@ -49,6 +49,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -69,7 +70,7 @@
     val transition = updateTransition(true)
 
     var entering by remember { mutableStateOf(true) }
-    var progress by remember { mutableStateOf(0f) }
+    var progress by remember { mutableFloatStateOf(0f) }
     Column(
         Modifier.fillMaxSize().padding(10.dp),
         verticalArrangement = Arrangement.spacedBy(20.dp)
diff --git a/compose/animation/animation/samples/src/main/AndroidManifest.xml b/compose/animation/animation/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index dc2f180..0000000
--- a/compose/animation/animation/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/animation/animation/src/androidAndroidTest/AndroidManifest.xml b/compose/animation/animation/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/animation/animation/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/animation/animation/src/androidMain/AndroidManifest.xml b/compose/animation/animation/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 34d825b..0000000
--- a/compose/animation/animation/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
index 06a90b9..d1ac4ac 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
@@ -41,6 +41,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -196,7 +197,7 @@
      * to 0f. Content with higher zIndex will be drawn over lower `zIndex`ed content. Among
      * content with the same index, the target content will be placed on top.
      */
-    var targetContentZIndex by mutableStateOf(targetContentZIndex)
+    var targetContentZIndex by mutableFloatStateOf(targetContentZIndex)
 
     /**
      * [sizeTransform] manages the expanding and shrinking of the container if there is any size
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt
index 2b1411e..b11c7c0 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt
@@ -21,7 +21,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.colorspace.ColorSpace
 import androidx.compose.ui.graphics.colorspace.ColorSpaces
-import kotlin.math.pow
 
 /**
  * A lambda that takes a [ColorSpace] and returns a converter that can both convert a [Color] to
@@ -32,34 +31,17 @@
     { colorSpace ->
         TwoWayConverter(
             convertToVector = { color ->
-                // TODO: use Oklab when it is public API
-                val colorXyz = color.convert(ColorSpaces.CieXyz)
-                val x = colorXyz.red
-                val y = colorXyz.green
-                val z = colorXyz.blue
-
-                val l = multiplyColumn(0, x, y, z, M1).pow(1f / 3f)
-                val a = multiplyColumn(1, x, y, z, M1).pow(1f / 3f)
-                val b = multiplyColumn(2, x, y, z, M1).pow(1f / 3f)
-                AnimationVector4D(color.alpha, l, a, b)
+                val (l, a, b, alpha) = color.convert(ColorSpaces.Oklab)
+                AnimationVector4D(alpha, l, a, b)
             },
-            convertFromVector = {
-                val l = it.v2.pow(3f)
-                val a = it.v3.pow(3f)
-                val b = it.v4.pow(3f)
-
-                val x = multiplyColumn(0, l, a, b, InverseM1)
-                val y = multiplyColumn(1, l, a, b, InverseM1)
-                val z = multiplyColumn(2, l, a, b, InverseM1)
-
-                val colorXyz = Color(
-                    alpha = it.v1.coerceIn(0f, 1f),
-                    red = x.coerceIn(-2f, 2f),
-                    green = y.coerceIn(-2f, 2f),
-                    blue = z.coerceIn(-2f, 2f),
-                    colorSpace = ColorSpaces.CieXyz // here we have the right color space
-                )
-                colorXyz.convert(colorSpace)
+            convertFromVector = { vector ->
+                Color(
+                    vector.v2.coerceIn(0f, 1f), // L (red)
+                    vector.v3.coerceIn(-0.5f, 0.5f), // a (blue)
+                    vector.v4.coerceIn(-0.5f, 0.5f), // b (green)
+                    vector.v1.coerceIn(0f, 1f), // alpha
+                    ColorSpaces.Oklab
+                ).convert(colorSpace)
             }
         )
     }
@@ -70,23 +52,5 @@
  * [ColorSpace].
  */
 val Color.Companion.VectorConverter:
-    (colorSpace: ColorSpace) -> TwoWayConverter<Color, AnimationVector4D>
-        get() = ColorToVector
-
-// These are utilities and constants to emulate converting to/from Oklab color space.
-// These can be removed when Oklab becomes public and we can use it directly in the conversion.
-private val M1 = floatArrayOf(
-    0.80405736f, 0.026893456f, 0.04586542f,
-    0.3188387f, 0.9319606f, 0.26299807f,
-    -0.11419419f, 0.05105356f, 0.83999807f
-)
-
-private val InverseM1 = floatArrayOf(
-    1.2485008f, -0.032856926f, -0.057883114f,
-    -0.48331892f, 1.1044513f, -0.3194066f,
-    0.19910365f, -0.07159331f, 1.202023f
-)
-
-private fun multiplyColumn(column: Int, x: Float, y: Float, z: Float, matrix: FloatArray): Float {
-    return x * matrix[column] + y * matrix[3 + column] + z * matrix[6 + column]
-}
\ No newline at end of file
+        (colorSpace: ColorSpace) -> TwoWayConverter<Color, AnimationVector4D>
+    get() = ColorToVector
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
index 93c840e..6c7d2fa 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
@@ -36,6 +36,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -938,7 +939,7 @@
         convertFromVector = { TransformOrigin(it.v1, it.v2) }
     )
 
-private val DefaultAlpha = mutableStateOf(1f)
+private val DefaultAlpha = mutableFloatStateOf(1f)
 private val DefaultAlphaAndScaleSpring = spring<Float>(stiffness = Spring.StiffnessMediumLow)
 
 private fun Modifier.slideInOut(
diff --git a/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
index 1134f6c..4a889dc 100644
--- a/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
+++ b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
@@ -63,48 +63,46 @@
             0f
         )
 
-        // all channels should clamp:
+        // all channels are clamped before conversion, the result of 3/3/3 in OkLab should be
+        // 1,0,0 in sRGB
+        val maxOkLab = Color(1.0f, 0.5f, 0.5f, 1.0f, ColorSpaces.Oklab).convert(ColorSpaces.Srgb)
         assertEquals(
-            1f,
+            maxOkLab.red,
             converter.convertFromVector(AnimationVector4D(1.0f, 3f, 3f, 3f)).red,
             0f
         )
         assertEquals(
-            1f,
+            maxOkLab.green,
             converter.convertFromVector(AnimationVector4D(1.0f, 3f, 3f, 3f)).green,
             0f
         )
         assertEquals(
-            1f,
+            maxOkLab.blue,
             converter.convertFromVector(AnimationVector4D(1.0f, 3f, 3f, 3f)).blue,
             0f
         )
 
-        // All channel below 0.0f clamps to 0.0f and the result is black
+        // all channels are clamped before conversion, the result of -3/-3/-3 in OkLab should be
+        // 0,0,1 in sRGB
+        val minOkLab = Color(0.0f, -0.5f, -0.5f, 1.0f, ColorSpaces.Oklab).convert(ColorSpaces.Srgb)
         assertEquals(
-            0f,
+            minOkLab.red,
             converter.convertFromVector(AnimationVector4D(1.0f, -3f, -3f, -3f))
                 .red,
             0f
         )
         assertEquals(
-            0f,
+            minOkLab.green,
             converter.convertFromVector(AnimationVector4D(1.0f, -3f, -3f, -3f))
                 .green,
             0f
         )
         assertEquals(
-            0f,
+            minOkLab.blue,
             converter.convertFromVector(AnimationVector4D(1.0f, -3f, -3f, -3f))
                 .blue,
             0f
         )
-
-        // Green channel above 1.0f clamps to 1.0f and the result is green
-        assertEquals(
-            converter.convertFromVector(AnimationVector4D(1.0f, 0.0f, 1.1f, 0f)),
-            Color.Green
-        )
     }
 
     @Test
diff --git a/compose/benchmark-utils/src/androidTest/AndroidManifest.xml b/compose/benchmark-utils/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e865f27..0000000
--- a/compose/benchmark-utils/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
index e3ca786..611c856 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
@@ -697,6 +697,7 @@
                 }
 
                 class Bar {
+                    @get:Composable
                     val foo by Foo()
                 }
 
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
index 9211731..b198df57 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -1363,7 +1363,10 @@
             }
 
             class Bar {
-                val foo by Foo()
+                val <!COMPOSABLE_EXPECTED!>foo<!> by Foo()
+
+                @get:Composable
+                val foo2 by Foo()
             }
             """
         )
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
index 848bf03..25c7023 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -50,8 +50,6 @@
 import org.jetbrains.kotlin.psi.KtTryExpression
 import org.jetbrains.kotlin.resolve.BindingContext
 import org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL
-import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
-import org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression
 import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
 import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
 import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
@@ -60,6 +58,8 @@
 import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
 import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
+import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
+import org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression
 import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinedArgument
 import org.jetbrains.kotlin.resolve.sam.getSingleAbstractMethodOrNull
 import org.jetbrains.kotlin.types.KotlinType
@@ -243,6 +243,10 @@
                                 // setValue delegate is not allowed for now.
                                 illegalComposableDelegate(context, reportOn)
                             }
+                            if (descriptor is PropertyDescriptor &&
+                                descriptor.getter?.hasComposableAnnotation() != true) {
+                                composableExpected(context, node.nameIdentifier ?: node)
+                            }
                             return
                         }
                     }
@@ -318,10 +322,17 @@
     ) {
         context.trace.report(ComposeErrors.COMPOSABLE_INVOCATION.on(callEl))
         if (functionEl != null) {
-            context.trace.report(ComposeErrors.COMPOSABLE_EXPECTED.on(functionEl))
+            composableExpected(context, functionEl)
         }
     }
 
+    private fun composableExpected(
+        context: CallCheckerContext,
+        functionEl: PsiElement
+    ) {
+        context.trace.report(ComposeErrors.COMPOSABLE_EXPECTED.on(functionEl))
+    }
+
     private fun illegalCallMustBeReadonly(
         context: CallCheckerContext,
         callEl: PsiElement
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
index 7bb80ae..fd4a4db 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
@@ -34,7 +34,6 @@
 import org.jetbrains.kotlin.ir.declarations.IrFunction
 import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
-import org.jetbrains.kotlin.ir.declarations.IrProperty
 import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
 import org.jetbrains.kotlin.ir.declarations.IrValueParameter
 import org.jetbrains.kotlin.ir.declarations.copyAttributes
@@ -174,18 +173,6 @@
         return super.visitLocalDelegatedProperty(declaration)
     }
 
-    override fun visitProperty(declaration: IrProperty): IrStatement {
-        if (declaration.getter?.isComposableDelegatedAccessor() == true) {
-            declaration.getter!!.annotations += createComposableAnnotation()
-        }
-
-        if (declaration.setter?.isComposableDelegatedAccessor() == true) {
-            declaration.setter!!.annotations += createComposableAnnotation()
-        }
-
-        return super.visitProperty(declaration)
-    }
-
     private fun createComposableAnnotation() =
         IrConstructorCallImpl(
             startOffset = SYNTHETIC_OFFSET,
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index a754486..872eef6 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,94 +23,66 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-
-        api("androidx.annotation:annotation:1.1.0")
-        api(project(":compose:ui:ui"))
-        api("androidx.compose.ui:ui-unit:1.2.1")
-
-        implementation("androidx.compose.runtime:runtime:1.2.1")
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-        implementation("androidx.core:core:1.7.0")
-        implementation("androidx.compose.animation:animation-core:1.2.1")
-        implementation(libs.kotlinStdlibCommon)
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.2.1")
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation("androidx.activity:activity-compose:1.3.1")
-        // old version of common-java8 conflicts with newer version, because both have
-        // DefaultLifecycleEventObserver.
-        // Outside of androidx this is resolved via constraint added to lifecycle-common,
-        // but it doesn't work in androidx.
-        // See aosp/1804059
-        androidTestImplementation("androidx.lifecycle:lifecycle-common-java8:2.5.1")
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-
-        samples(project(":compose:foundation:foundation-layout:foundation-layout-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
 
                 api(project(":compose:ui:ui"))
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            androidMain.dependencies {
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.annotation:annotation:1.1.0")
                 implementation("androidx.core:core:1.7.0")
                 implementation("androidx.compose.animation:animation-core:1.2.1")
             }
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+
+                    implementation(project(":compose:runtime:runtime"))
+                    implementation(project(":compose:ui:ui-util"))
+                }
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
+        jvmTest {
+            dependencies {
             }
+        }
 
-            androidAndroidTest.dependencies {
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:foundation:foundation"))
-                implementation(project(":compose:ui:ui-test-junit4"))
+                implementation("androidx.compose.ui:ui-test-junit4:1.2.1")
                 implementation(project(":compose:test-utils"))
                 implementation("androidx.activity:activity-compose:1.3.1")
 
@@ -121,9 +92,26 @@
                 implementation(libs.truth)
             }
         }
-    }
-    dependencies {
-        samples(project(":compose:foundation:foundation-layout:foundation-layout-samples"))
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+                implementation(libs.truth)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+            }
+        }
     }
 }
 
diff --git a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index d04f9bb..0000000
--- a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2016 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/samples/src/main/AndroidManifest.xml b/compose/foundation/foundation-layout/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index b962196..0000000
--- a/compose/foundation/foundation-layout/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
index 7356bd9..cee92f7 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
@@ -53,6 +53,7 @@
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -1836,6 +1837,7 @@
         )
     }
 
+    @Ignore // b/281171119
     @Test
     fun testModifiers_doNotCauseUnnecessaryRemeasure() {
         var first by mutableStateOf(true)
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsAnimationTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsAnimationTest.kt
index fab47f7..9cabe4e 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsAnimationTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsAnimationTest.kt
@@ -31,6 +31,7 @@
 import java.util.concurrent.TimeUnit
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 
@@ -57,6 +58,7 @@
     @SdkSuppress(minSdkVersion = 22) // b/266742122
     @OptIn(ExperimentalLayoutApi::class)
     @Test
+    @Ignore("b/266742122")
     fun imeAnimationWhenShowingIme() {
         val imeAnimationSourceValues = mutableListOf<Int>()
         val imeAnimationTargetValues = mutableListOf<Int>()
diff --git a/compose/foundation/foundation-layout/src/androidMain/AndroidManifest.xml b/compose/foundation/foundation-layout/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index b47339d..0000000
--- a/compose/foundation/foundation-layout/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index c37d48d..260f499 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -777,6 +777,7 @@
     method public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> getIntervals();
     method public final int getItemCount();
     method public final Object getKey(int index);
+    method public final inline <T> T withInterval(int globalIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super Interval,? extends T> block);
     property public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> intervals;
     property public final int itemCount;
   }
@@ -788,12 +789,8 @@
     property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> type;
   }
 
-  public final class LazyLayoutIntervalContentKt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static <T extends androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.Interval> void PinnableItem(androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent<T>, int index, androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList pinnedItemList, kotlin.jvm.functions.Function2<? super T,? super java.lang.Integer,kotlin.Unit> content);
-  }
-
   @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface LazyLayoutItemProvider {
-    method @androidx.compose.runtime.Composable public void Item(int index);
+    method @androidx.compose.runtime.Composable public void Item(int index, Object key);
     method public default Object? getContentType(int index);
     method public default int getIndex(Object key);
     method public int getItemCount();
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index e367382..a57dfef 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -15,9 +15,8 @@
  */
 
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -26,80 +25,15 @@
     id("AndroidXPaparazziPlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        api("androidx.annotation:annotation:1.1.0")
-        api("androidx.compose.animation:animation:1.2.1")
-        api(project(":compose:runtime:runtime"))
-        api(project(":compose:ui:ui"))
-
-        implementation(libs.kotlinStdlibCommon)
-        implementation(project(":compose:foundation:foundation-layout"))
-        implementation(project(':emoji2:emoji2'))
-        implementation(project(':core:core'))
-        implementation("androidx.compose.ui:ui-graphics:1.2.1")
-        implementation("androidx.compose.ui:ui-text:1.2.1")
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-
-        testImplementation(project(":compose:test-utils"))
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-        testImplementation(libs.kotlinCoroutinesTest)
-        testImplementation(libs.kotlinTest)
-        testImplementation(libs.mockitoCore4)
-        testImplementation(libs.kotlinReflect)
-        testImplementation(libs.mockitoKotlin4)
-
-        testImplementation(project(":constraintlayout:constraintlayout-compose"))
-
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":internal-testutils-fonts"))
-        androidTestImplementation(project(":test:screenshot:screenshot"))
-        androidTestImplementation(project(":internal-testutils-runtime"))
-        androidTestImplementation(libs.testUiautomator)
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testMonitor)
-        androidTestImplementation "androidx.activity:activity-compose:1.3.1"
-        androidTestImplementation "androidx.lifecycle:lifecycle-runtime:2.6.1"
-        androidTestImplementation "androidx.savedstate:savedstate:1.2.1"
-        androidTestImplementation(libs.espressoCore)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.kotlinTest)
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.mockitoKotlin)
-
-        lintChecks(project(":compose:foundation:foundation-lint"))
-        lintPublish(project(":compose:foundation:foundation-lint"))
-
-        samples(project(":compose:foundation:foundation:foundation-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(':compose:animation:animation'))
                 api(project(':compose:runtime:runtime'))
@@ -108,36 +42,57 @@
                 implementation(project(":compose:ui:ui-util"))
                 implementation(project(':compose:foundation:foundation-layout'))
             }
-            androidMain.dependencies {
-                api("androidx.annotation:annotation:1.1.0")
-                implementation(project(':emoji2:emoji2'))
-                implementation(project(":core:core"))
-            }
+        }
+        androidMain.dependencies {
+            api("androidx.annotation:annotation:1.1.0")
+            implementation(project(':emoji2:emoji2'))
+            implementation(project(":core:core"))
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
-            }
-
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.mockitoCore4)
-                implementation(libs.truth)
-                implementation(libs.kotlinReflect)
-                implementation(libs.mockitoKotlin4)
-            }
-
-            commonTest.dependencies {
+        commonTest {
+            dependencies {
                 implementation(libs.kotlinTest)
                 implementation(libs.kotlinCoroutinesTest)
             }
+        }
 
-            androidAndroidTest.dependencies {
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api("androidx.annotation:annotation:1.1.0")
+                implementation(project(':emoji2:emoji2'))
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+
+                    implementation(project(":compose:ui:ui-util"))
+                }
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+                implementation(project(":compose:ui:ui-test"))
+                implementation(project(":compose:ui:ui-test-junit4"))
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:test-utils"))
                 implementation(project(":internal-testutils-fonts"))
                 implementation(project(":test:screenshot:screenshot"))
@@ -157,20 +112,43 @@
                 implementation(libs.mockitoCore)
                 implementation(libs.mockitoKotlin)
             }
+        }
 
-            desktopTest.dependencies {
-                implementation(project(":compose:ui:ui-test-junit4"))
-                implementation(libs.truth)
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
                 implementation(libs.junit)
-                implementation(libs.skikoCurrentOs)
-                implementation(libs.mockitoCore)
-                implementation(libs.mockitoKotlin)
+                implementation(libs.truth)
+                implementation(libs.kotlinReflect)
+                implementation(project(":constraintlayout:constraintlayout-compose"))
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                    implementation(libs.truth)
+                    implementation(libs.junit)
+                    implementation(libs.skikoCurrentOs)
+                    implementation(libs.mockitoCore)
+                    implementation(libs.mockitoKotlin)
+                }
             }
         }
     }
-    dependencies {
-        samples(project(":compose:foundation:foundation:foundation-samples"))
-    }
+}
+
+dependencies {
+    lintChecks(project(":compose:foundation:foundation-lint"))
+    lintPublish(project(":compose:foundation:foundation-lint"))
 }
 
 // Screenshot tests related setup
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a69672..0000000
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/DrawModifiersDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/DrawModifiersDemo.kt
index 0698db6..0c0c3eb 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/DrawModifiersDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/DrawModifiersDemo.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.samples.BorderSample
 import androidx.compose.foundation.samples.BorderSampleWithBrush
 import androidx.compose.foundation.samples.BorderSampleWithDataClass
+import androidx.compose.foundation.samples.BorderSampleWithDynamicData
 import androidx.compose.foundation.samples.DrawBackgroundColor
 import androidx.compose.foundation.samples.DrawBackgroundShapedBrush
 import androidx.compose.runtime.Composable
@@ -41,6 +42,8 @@
             BorderSampleWithBrush()
             Spacer(Modifier.height(30.dp))
             BorderSampleWithDataClass()
+            Spacer(Modifier.height(30.dp))
+            BorderSampleWithDynamicData()
         }
         Column(Modifier.weight(1f).padding(10.dp)) {
             DrawBackgroundColor()
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt
index 5993f24..303631a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt
@@ -40,6 +40,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -117,8 +119,8 @@
 
     internal val scrollChannel = Channel<Float>()
 
-    private var draggingItemDraggedDelta by mutableStateOf(0f)
-    private var draggingItemInitialOffset by mutableStateOf(0)
+    private var draggingItemDraggedDelta by mutableFloatStateOf(0f)
+    private var draggingItemInitialOffset by mutableIntStateOf(0)
     internal val draggingItemOffset: Float
         get() = draggingItemLayoutInfo?.let { item ->
             draggingItemInitialOffset + draggingItemDraggedDelta - item.offset
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index c7d9378..4634941 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -80,6 +80,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateMapOf
 import androidx.compose.runtime.mutableStateOf
@@ -158,8 +159,8 @@
 @Preview
 @Composable
 private fun ListAddRemoveItemsDemo() {
-    var numItems by remember { mutableStateOf(0) }
-    var offset by remember { mutableStateOf(0) }
+    var numItems by remember { mutableIntStateOf(0) }
+    var offset by remember { mutableIntStateOf(0) }
     Column {
         Row {
             val buttonModifier = Modifier.padding(8.dp)
@@ -406,8 +407,8 @@
 
 @Composable
 private fun LazyListArrangements() {
-    var count by remember { mutableStateOf(3) }
-    var arrangement by remember { mutableStateOf(6) }
+    var count by remember { mutableIntStateOf(3) }
+    var arrangement by remember { mutableIntStateOf(6) }
     Column {
         Row {
             Button(onClick = { count-- }) {
@@ -480,7 +481,7 @@
     Column {
         val scrollState = rememberScrollState()
         val lazyState = rememberLazyListState()
-        var count by remember { mutableStateOf(10) }
+        var count by remember { mutableIntStateOf(10) }
         var reverse by remember { mutableStateOf(false) }
         var rtl by remember { mutableStateOf(false) }
         var column by remember { mutableStateOf(true) }
@@ -634,7 +635,7 @@
                 .background(Color.LightGray),
             contentAlignment = Alignment.Center
         ) {
-            var state by rememberSaveable { mutableStateOf(0) }
+            var state by rememberSaveable { mutableIntStateOf(0) }
             Button(onClick = { state++ }) {
                 Text("Index=$index State=$state")
             }
@@ -660,7 +661,7 @@
         GridCells.Fixed(3),
         GridCells.Adaptive(minSize = 60.dp)
     )
-    var currentMode by remember { mutableStateOf(0) }
+    var currentMode by remember { mutableIntStateOf(0) }
     Column {
         Button(
             modifier = Modifier.wrapContentSize(),
@@ -709,10 +710,10 @@
             }
         }
     )
-    var currentMode by remember { mutableStateOf(0) }
-    var horizontalSpacing by remember { mutableStateOf(8) }
+    var currentMode by remember { mutableIntStateOf(0) }
+    var horizontalSpacing by remember { mutableIntStateOf(8) }
     var horizontalSpacingExpanded by remember { mutableStateOf(false) }
-    var verticalSpacing by remember { mutableStateOf(8) }
+    var verticalSpacing by remember { mutableIntStateOf(8) }
     var verticalSpacingExpanded by remember { mutableStateOf(false) }
     Column {
         Row {
@@ -860,7 +861,7 @@
         }
         LazyColumn {
             item {
-                var counter by rememberSaveable { mutableStateOf(0) }
+                var counter by rememberSaveable { mutableIntStateOf(0) }
                 Button(onClick = { counter++ }) {
                     Text("Header has $counter")
                 }
@@ -869,7 +870,7 @@
                 items = names,
                 key = { it }
             ) {
-                var counter by rememberSaveable { mutableStateOf(0) }
+                var counter by rememberSaveable { mutableIntStateOf(0) }
                 Button(onClick = { counter++ }, modifier = Modifier.animateItemPlacement()) {
                     Text("$it has $counter")
                 }
@@ -962,7 +963,7 @@
         mutableStateOf(List(100) { it })
     }
 
-    var count by remember { mutableStateOf(10) }
+    var count by remember { mutableIntStateOf(10) }
     var reverseLayout by remember { mutableStateOf(false) }
     var rtl by remember { mutableStateOf(false) }
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ScrollableFocusedChildDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ScrollableFocusedChildDemo.kt
index 8ba6e1e..92d0c13 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ScrollableFocusedChildDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ScrollableFocusedChildDemo.kt
@@ -41,6 +41,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -153,8 +154,8 @@
 }
 
 private class ResizableState {
-    var widthOverride by mutableStateOf(-1)
-    var heightOverride by mutableStateOf(-1)
+    var widthOverride by mutableIntStateOf(-1)
+    var heightOverride by mutableIntStateOf(-1)
 
     fun resetToMaxSize() {
         widthOverride = -1
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
index 183a7d9..6bae60a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
@@ -43,6 +43,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -109,12 +110,12 @@
  */
 @Composable
 fun CoroutineTapDemo() {
-    var tapHue by remember { mutableStateOf(randomHue()) }
-    var longPressHue by remember { mutableStateOf(randomHue()) }
-    var doubleTapHue by remember { mutableStateOf(randomHue()) }
-    var pressHue by remember { mutableStateOf(randomHue()) }
-    var releaseHue by remember { mutableStateOf(randomHue()) }
-    var cancelHue by remember { mutableStateOf(randomHue()) }
+    var tapHue by remember { mutableFloatStateOf(randomHue()) }
+    var longPressHue by remember { mutableFloatStateOf(randomHue()) }
+    var doubleTapHue by remember { mutableFloatStateOf(randomHue()) }
+    var pressHue by remember { mutableFloatStateOf(randomHue()) }
+    var releaseHue by remember { mutableFloatStateOf(randomHue()) }
+    var cancelHue by remember { mutableFloatStateOf(randomHue()) }
 
     Column {
         Text("The boxes change color when you tap the white box.")
@@ -214,7 +215,7 @@
 @Composable
 fun TouchSlopDragGestures() {
     Column {
-        var width by remember { mutableStateOf(0f) }
+        var width by remember { mutableFloatStateOf(0f) }
         Box(
             Modifier.fillMaxWidth()
                 .background(Color.Cyan)
@@ -241,7 +242,7 @@
         }
 
         Box(Modifier.weight(1f)) {
-            var height by remember { mutableStateOf(0f) }
+            var height by remember { mutableFloatStateOf(0f) }
             Box(
                 Modifier.fillMaxHeight()
                     .background(Color.Yellow)
@@ -328,23 +329,23 @@
 @Composable
 fun Drag2DGestures() {
     var size by remember { mutableStateOf(IntSize.Zero) }
-    val offsetX = remember { mutableStateOf(0f) }
-    val offsetY = remember { mutableStateOf(0f) }
+    var offsetX by remember { mutableFloatStateOf(0f) }
+    var offsetY by remember { mutableFloatStateOf(0f) }
     Box(
         Modifier.onSizeChanged {
             size = it
         }.fillMaxSize()
     ) {
         Box(
-            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
+            Modifier.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                 .background(Color.Blue)
                 .requiredSize(50.dp)
                 .pointerInput(Unit) {
                     detectDragGestures { _, dragAmount ->
-                        offsetX.value = (offsetX.value + dragAmount.x)
+                        offsetX = (offsetX + dragAmount.x)
                             .coerceIn(0f, size.width.toFloat() - 50.dp.toPx())
 
-                        offsetY.value = (offsetY.value + dragAmount.y)
+                        offsetY = (offsetY + dragAmount.y)
                             .coerceIn(0f, size.height.toFloat() - 50.dp.toPx())
                     }
                 }
@@ -361,10 +362,10 @@
     ) -> Unit
 ) {
     val matrix by remember { mutableStateOf(Matrix()) }
-    var angle by remember { mutableStateOf(0f) }
-    var zoom by remember { mutableStateOf(1f) }
-    var offsetX by remember { mutableStateOf(0f) }
-    var offsetY by remember { mutableStateOf(0f) }
+    var angle by remember { mutableFloatStateOf(0f) }
+    var zoom by remember { mutableFloatStateOf(1f) }
+    var offsetX by remember { mutableFloatStateOf(0f) }
+    var offsetY by remember { mutableFloatStateOf(0f) }
 
     Box(
         Modifier.fillMaxSize().pointerInput(Unit) {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeLineHeight.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeLineHeight.kt
index 085258f..a91e0d2 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeLineHeight.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeLineHeight.kt
@@ -36,6 +36,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -70,8 +71,8 @@
         Modifier.verticalScroll(rememberScrollState())
             .background(TextMetricColors.Default.background)
     ) {
-        var lineHeightSp = remember { mutableStateOf(60f) }
-        var lineHeightEm = remember { mutableStateOf(1f) }
+        var lineHeightSp = remember { mutableFloatStateOf(60f) }
+        var lineHeightEm = remember { mutableFloatStateOf(1f) }
         var lineHeightEnabled = remember { mutableStateOf(false) }
         val lineHeightStyleEnabled = remember { mutableStateOf(false) }
         var lineHeightAlignment = remember {
@@ -97,8 +98,8 @@
             Spacer(Modifier.padding(16.dp))
             TextWithLineHeight(
                 lineHeightEnabled.value,
-                lineHeightSp.value,
-                lineHeightEm.value,
+                lineHeightSp.floatValue,
+                lineHeightEm.floatValue,
                 if (lineHeightStyleEnabled.value) {
                     LineHeightStyle(
                         alignment = lineHeightAlignment.value,
@@ -336,7 +337,7 @@
     snap: Boolean = true,
     enabled: Boolean = true
 ) {
-    var lastValue by remember(value) { mutableStateOf(value) }
+    var lastValue by remember(value) { mutableFloatStateOf(value) }
     val increment = valueRange.endInclusive / (steps + 1).toFloat()
     val snapValue = round(value / increment / 2f) * increment
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeText.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeText.kt
index bf5e4dd..709067a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeText.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeText.kt
@@ -54,6 +54,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -562,7 +563,7 @@
     val textStyleHyphensOff = TextStyle(fontSize = fontSize8, color = Color.Blue,
         hyphens = Hyphens.None)
     Column {
-        var width by remember { mutableStateOf(30f) }
+        var width by remember { mutableFloatStateOf(30f) }
         Slider(
             value = width,
             onValueChange = { width = it },
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/FontFamilyDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/FontFamilyDemo.kt
index d7aae18..0b21ea8 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/FontFamilyDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/FontFamilyDemo.kt
@@ -28,6 +28,7 @@
 import androidx.compose.material.TextField
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -64,7 +65,7 @@
 @Composable
 fun AsyncFontFamilyDemo() {
 
-    var recreateFontFamily by remember { mutableStateOf(0) }
+    var recreateFontFamily by remember { mutableIntStateOf(0) }
     var showW800 by remember {
         mutableStateOf(false)
     }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/InteractiveText.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/InteractiveText.kt
index b671f6b..57d039b 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/InteractiveText.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/InteractiveText.kt
@@ -25,8 +25,11 @@
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.AnnotatedString
@@ -38,7 +41,7 @@
 fun InteractiveTextDemo() {
     val clickedOffset = remember { mutableStateOf<Int?>(null) }
     val hoveredOffset = remember { mutableStateOf<Int?>(null) }
-    val numOnHoverInvocations = remember { mutableStateOf(0) }
+    var numOnHoverInvocations by remember { mutableIntStateOf(0) }
     Column(
         modifier = Modifier.padding(horizontal = 10.dp)
     ) {
@@ -47,14 +50,14 @@
         Text(text = "Click/Hover the lorem ipsum text below.")
         Text(text = "Clicked offset: ${clickedOffset.value ?: "No click yet"}")
         Text(text = "Hovered offset: ${hoveredOffset.value ?: "Not hovering"}")
-        Text(text = "Number of onHover invocations: ${numOnHoverInvocations.value}")
+        Text(text = "Number of onHover invocations: $numOnHoverInvocations")
 
         ClickableText(
             text = AnnotatedString(loremIpsum(wordCount = 30)),
             modifier = Modifier.border(Dp.Hairline, Color.Black),
             style = MaterialTheme.typography.body1,
             onHover = {
-                numOnHoverInvocations.value = numOnHoverInvocations.value + 1
+                numOnHoverInvocations++
                 hoveredOffset.value = it
             }
         ) { offset ->
@@ -65,7 +68,7 @@
             onClick = {
                 clickedOffset.value = null
                 hoveredOffset.value = null
-                numOnHoverInvocations.value = 0
+                numOnHoverInvocations = 0
             }
         ) {
             Text(text = "Reset Offsets/Counter")
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LetterSpacingDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LetterSpacingDemo.kt
index 654faea..045a0c1 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LetterSpacingDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LetterSpacingDemo.kt
@@ -26,6 +26,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -42,8 +43,8 @@
     Column(
         Modifier.padding(horizontal = 16.dp)
     ) {
-        var letterSpacing: Float by remember { mutableStateOf(0.0f) }
-        var fontSize: Float by remember { mutableStateOf(12f) }
+        var letterSpacing: Float by remember { mutableFloatStateOf(0.0f) }
+        var fontSize: Float by remember { mutableFloatStateOf(12f) }
 
         Text("LetterSpacing: ${letterSpacing.toString().take(4)}.sp")
         Slider(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LineBreakDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LineBreakDemo.kt
index 55ccf56..f571dc0 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LineBreakDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LineBreakDemo.kt
@@ -26,8 +26,10 @@
 import androidx.compose.material.Slider
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.SpanStyle
@@ -55,13 +57,13 @@
 
 @Composable
 fun TextLineBreakDemo() {
-    val selectedFontSize = remember { mutableStateOf(16f) }
+    var selectedFontSize by remember { mutableFloatStateOf(16f) }
 
     Column(modifier = Modifier.fillMaxSize()) {
-        Text("Font size: ${selectedFontSize.value}")
+        Text("Font size: $selectedFontSize")
         Slider(
-            value = selectedFontSize.value,
-            onValueChange = { value -> selectedFontSize.value = value },
+            value = selectedFontSize,
+            onValueChange = { value -> selectedFontSize = value },
             valueRange = 8f..48f
         )
 
@@ -82,7 +84,7 @@
                     },
                     style = TextStyle(
                         lineBreak = preset,
-                        fontSize = selectedFontSize.value.sp
+                        fontSize = selectedFontSize.sp
                     ),
                     modifier = textModifier.weight(1f)
                 )
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/StrokeDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/StrokeDemo.kt
index c3c5211..bc0cda0 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/StrokeDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/StrokeDemo.kt
@@ -27,6 +27,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -45,7 +46,7 @@
 @OptIn(ExperimentalTextApi::class)
 @Composable
 fun TextStrokeDemo() {
-    var dashInterval by remember { mutableStateOf(2f) }
+    var dashInterval by remember { mutableFloatStateOf(2f) }
     var stroke by remember {
         mutableStateOf(
             Stroke(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInDialogDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInDialogDemo.kt
index bce2a5f..15f6d1e 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInDialogDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInDialogDemo.kt
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -106,7 +107,7 @@
 @Composable
 fun TextFieldsInDialogDemo() {
     val listState = rememberLazyListState()
-    val (currentDemoIndex, setDemoIndex) = rememberSaveable { mutableStateOf(-1) }
+    val (currentDemoIndex, setDemoIndex) = rememberSaveable { mutableIntStateOf(-1) }
 
     if (currentDemoIndex == -1) {
         LazyColumn(state = listState) {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/VariableFontsDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/VariableFontsDemo.kt
index ba89fe4..843f8fe 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/VariableFontsDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/VariableFontsDemo.kt
@@ -37,6 +37,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -65,7 +66,7 @@
         Text("Variable fonts are only supported on API 26+")
      }
 
-    val (weight, setWeight) = remember { mutableStateOf(1000f) }
+    val (weight, setWeight) = remember { mutableFloatStateOf(1000f) }
     val (italic, setItalic) = remember { mutableStateOf(false) }
     LazyColumn {
         this.stickyHeader {
diff --git a/compose/foundation/foundation/samples/src/main/AndroidManifest.xml b/compose/foundation/foundation/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a69672..0000000
--- a/compose/foundation/foundation/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt
index 304227b..e42bb65 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt
@@ -19,23 +19,35 @@
 import androidx.annotation.Sampled
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.TileMode
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 
 @Composable
 @Sampled
 fun BorderSample() {
     Text(
         "Text with  square border",
-        modifier = Modifier.border(4.dp, Color.Magenta).padding(10.dp)
+        modifier = Modifier
+            .border(4.dp, Color.Magenta)
+            .padding(10.dp)
     )
 }
 
@@ -50,7 +62,8 @@
     )
     Text(
         "Text with gradient border",
-        modifier = Modifier.border(width = 2.dp, brush = gradientBrush, shape = CircleShape)
+        modifier = Modifier
+            .border(width = 2.dp, brush = gradientBrush, shape = CircleShape)
             .padding(10.dp)
     )
 }
@@ -60,9 +73,102 @@
 fun BorderSampleWithDataClass() {
     Text(
         "Text with gradient border",
-        modifier = Modifier.border(
-            border = BorderStroke(2.dp, Color.Blue),
-            shape = CutCornerShape(8.dp)
-        ).padding(10.dp)
+        modifier = Modifier
+            .border(
+                border = BorderStroke(2.dp, Color.Blue),
+                shape = CutCornerShape(8.dp)
+            )
+            .padding(10.dp)
     )
-}
\ No newline at end of file
+}
+
+@Composable
+@Sampled
+fun BorderSampleWithDynamicData() {
+    val widthRange = (1..10)
+    var width by remember { mutableStateOf((widthRange.random()).dp) }
+
+    val shapes = remember {
+        listOf(CutCornerShape(8.dp), CircleShape, RoundedCornerShape(20))
+    }
+    var selectedShape by remember { mutableStateOf(shapes.random()) }
+
+    val colors = listOf(
+        Color.Black,
+        Color.DarkGray,
+        Color.Gray,
+        Color.LightGray,
+        Color.White,
+        Color.Red,
+        Color.Blue,
+        Color.Green,
+        Color.Yellow,
+        Color.Cyan,
+        Color.Magenta
+    )
+    var gradientBrush by remember {
+        mutableStateOf(
+            Brush.horizontalGradient(
+                colors = listOf(colors.random(), colors.random(), colors.random()),
+                startX = 0.0f,
+                endX = 500.0f,
+                tileMode = TileMode.Repeated
+            )
+        )
+    }
+
+    Column(Modifier.padding(2.dp)) {
+        Text(text = "Update border with buttons")
+        Row {
+            Button(
+                modifier = Modifier.width(60.dp),
+                onClick = {
+
+                    width = (widthRange.random()).dp
+                }
+            ) {
+                Text(
+                    fontSize = 8.sp,
+                    text = "width"
+                )
+            }
+            Button(
+                modifier = Modifier.width(60.dp),
+                onClick = {
+                    gradientBrush = Brush.horizontalGradient(
+                        colors = listOf(colors.random(), colors.random(), colors.random()),
+                        startX = 0.0f,
+                        endX = 500.0f,
+                        tileMode = TileMode.Repeated
+                    )
+                }
+            ) {
+                Text(
+                    fontSize = 8.sp,
+                    text = "brush"
+                )
+            }
+            Button(
+                modifier = Modifier.width(60.dp),
+                onClick = {
+                    selectedShape = shapes.random()
+                }
+            ) {
+                Text(
+                    fontSize = 8.sp,
+                    text = "shape"
+                )
+            }
+        }
+        Text(
+            "Dynamic border",
+            modifier = Modifier
+                .border(
+                    width = width,
+                    brush = gradientBrush,
+                    shape = selectedShape
+                )
+                .padding(10.dp)
+        )
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt
index 1bf2f65..7cc04da 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt
@@ -21,9 +21,12 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -180,6 +183,42 @@
     }
 
     @Test
+    fun background_changeShape() {
+        var shape by mutableStateOf(RoundedCornerShape(10f))
+
+        rule.setContent {
+            SemanticParent {
+                Box(
+                    Modifier.size(40f.toDp())
+                        .background(Color.Magenta)
+                        .background(color = Color.White, shape = shape)
+                )
+            }
+        }
+
+        val bitmap = rule.onNodeWithTag(contentTag).captureToImage()
+        bitmap.assertShape(
+            density = rule.density,
+            backgroundColor = Color.Magenta,
+            shape = RoundedCornerShape(10f),
+            shapeColor = Color.White,
+            shapeOverlapPixelCount = 2.0f
+        )
+
+        shape = CircleShape
+        rule.waitForIdle()
+
+        val bitmap2 = rule.onNodeWithTag(contentTag).captureToImage()
+        bitmap2.assertShape(
+            density = rule.density,
+            backgroundColor = Color.Magenta,
+            shape = CircleShape,
+            shapeColor = Color.White,
+            shapeOverlapPixelCount = 2.0f
+        )
+    }
+
+    @Test
     fun background_rtl_initially() {
         rule.setContent {
             SemanticParent {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyCustomKeysTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyCustomKeysTest.kt
index aa07638..76103d9 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyCustomKeysTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyCustomKeysTest.kt
@@ -422,6 +422,26 @@
         }
     }
 
+    @Test
+    fun keysLambdaIsCalledOnlyOnce() {
+        var keyCalls = 0
+
+        rule.setContent {
+            LazyVerticalGrid(GridCells.Fixed(1)) {
+                items(1, key = {
+                    keyCalls++
+                    0
+                }) {
+                    Item("item")
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(keyCalls).isEqualTo(1)
+        }
+    }
+
     private fun testReordering(content: LazyGridScope.(List<MyClass>) -> Unit) {
         var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt
index e8dcc77..d05d1f1 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt
@@ -305,7 +305,7 @@
                     override val itemCount: Int = itemCount()
 
                     @Composable
-                    override fun Item(index: Int) {
+                    override fun Item(index: Int, key: Any) {
                         content(index)
                     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
index 603ac21..c9c1a87 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
@@ -23,6 +23,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -467,13 +468,47 @@
         }
     }
 
+    @Test
+    fun skippingItemBlockWhenKeyIsObservableButDidntChange() {
+        val stateList = mutableStateListOf(0)
+        var itemCalls = 0
+        val itemProvider = object : LazyLayoutItemProvider {
+            @Composable
+            override fun Item(index: Int, key: Any) {
+                assertThat(index).isEqualTo(0)
+                assertThat(key).isEqualTo(index)
+                itemCalls++
+            }
+
+            override val itemCount: Int get() = stateList.size
+
+            override fun getKey(index: Int) = stateList[index]
+        }
+        rule.setContent {
+            LazyLayout(itemProvider) { constraint ->
+                measure(0, constraint)
+                layout(100, 100) {}
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(itemCalls).isEqualTo(1)
+
+            stateList += 1
+        }
+
+        rule.runOnIdle {
+            assertThat(itemCalls).isEqualTo(1)
+        }
+    }
+
     private fun itemProvider(
         itemCount: () -> Int,
         itemContent: @Composable (Int) -> Unit
     ): LazyLayoutItemProvider {
         return object : LazyLayoutItemProvider {
             @Composable
-            override fun Item(index: Int) {
+            override fun Item(index: Int, key: Any) {
                 itemContent(index)
             }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt
index fcb0408..9c97094 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt
@@ -429,6 +429,26 @@
         }
     }
 
+    @Test
+    fun keysLambdaIsCalledOnlyOnce() {
+        var keyCalls = 0
+
+        rule.setContent {
+            LazyColumn {
+                items(1, key = {
+                    keyCalls++
+                    0
+                }) {
+                    Item("item")
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(keyCalls).isEqualTo(1)
+        }
+    }
+
     private fun testReordering(content: LazyListScope.(List<MyClass>) -> Unit) {
         var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCustomKeysTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCustomKeysTest.kt
new file mode 100644
index 0000000..554a393
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCustomKeysTest.kt
@@ -0,0 +1,508 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class LazyCustomKeysTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    val itemSize = with(rule.density) {
+        100.toDp()
+    }
+    val columns = 2
+
+    @Test
+    fun itemsWithKeysAreLaidOutCorrectly() {
+        val list = listOf(MyClass(0), MyClass(1), MyClass(2))
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                items(list, key = { it.id }) {
+                    Item("${it.id}")
+                }
+            }
+        }
+
+        assertItems("0", "1", "2")
+    }
+
+    @Test
+    fun removing_statesAreMoved() {
+        var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                items(list, key = { it.id }) {
+                    Item(remember { "${it.id}" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = listOf(list[0], list[2])
+        }
+
+        assertItems("0", "2")
+    }
+
+    @Test
+    fun reordering_statesAreMoved_list() {
+        testReordering { grid ->
+            items(grid, key = { it.id }) {
+                Item(remember { "${it.id}" })
+            }
+        }
+    }
+
+    @Test
+    fun reordering_statesAreMoved_list_indexed() {
+        testReordering { grid ->
+            itemsIndexed(grid, key = { _, item -> item.id }) { _, item ->
+                Item(remember { "${item.id}" })
+            }
+        }
+    }
+
+    @Test
+    fun reordering_statesAreMoved_array() {
+        testReordering { grid ->
+            val array = grid.toTypedArray()
+            items(array, key = { it.id }) {
+                Item(remember { "${it.id}" })
+            }
+        }
+    }
+
+    @Test
+    fun reordering_statesAreMoved_array_indexed() {
+        testReordering { grid ->
+            val array = grid.toTypedArray()
+            itemsIndexed(array, key = { _, item -> item.id }) { _, item ->
+                Item(remember { "${item.id}" })
+            }
+        }
+    }
+
+    @Test
+    fun reordering_statesAreMoved_itemsWithCount() {
+        testReordering { grid ->
+            items(grid.size, key = { grid[it].id }) {
+                Item(remember { "${grid[it].id}" })
+            }
+        }
+    }
+
+    @Test
+    fun fullyReplacingTheList() {
+        var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
+        var counter = 0
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                items(list, key = { it.id }) {
+                    Item(remember { counter++ }.toString())
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = listOf(MyClass(3), MyClass(4), MyClass(5), MyClass(6))
+        }
+
+        assertItems("3", "4", "5", "6")
+    }
+
+    @Test
+    fun keepingOneItem() {
+        var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
+        var counter = 0
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                items(list, key = { it.id }) {
+                    Item(remember { counter++ }.toString())
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = listOf(MyClass(1))
+        }
+
+        assertItems("1")
+    }
+
+    @Test
+    fun keepingOneItemAndAddingMore() {
+        var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
+        var counter = 0
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                items(list, key = { it.id }) {
+                    Item(remember { counter++ }.toString())
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = listOf(MyClass(1), MyClass(3))
+        }
+
+        assertItems("1", "3")
+    }
+
+    @Test
+    fun mixingKeyedItemsAndNot() {
+        testReordering { list ->
+            item {
+                Item("${list.first().id}")
+            }
+            items(list.subList(fromIndex = 1, toIndex = list.size), key = { it.id }) {
+                Item(remember { "${it.id}" })
+            }
+        }
+    }
+
+    @Test
+    fun updatingTheDataSetIsCorrectlyApplied() {
+        val state = mutableStateOf(emptyList<Int>())
+
+        rule.setContent {
+            LaunchedEffect(Unit) {
+                state.value = listOf(4, 1, 3)
+            }
+
+            val list = state.value
+
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns), Modifier.fillMaxSize()) {
+                items(list, key = { it }) {
+                    Item(it.toString())
+                }
+            }
+        }
+
+        assertItems("4", "1", "3")
+
+        rule.runOnIdle {
+            state.value = listOf(2, 4, 6, 1, 3, 5)
+        }
+
+        assertItems("2", "4", "6", "1", "3", "5")
+    }
+
+    @Test
+    fun reordering_usingMutableStateListOf() {
+        val list = mutableStateListOf(MyClass(0), MyClass(1), MyClass(2))
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                items(list, key = { it.id }) {
+                    Item(remember { "${it.id}" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list.add(list.removeAt(1))
+        }
+
+        assertItems("0", "2", "1")
+    }
+
+    @Test
+    fun keysInLazyListItemInfoAreCorrect() {
+        val list = listOf(MyClass(0), MyClass(1), MyClass(2))
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState()
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns), state = state) {
+                items(list, key = { it.id }) {
+                    Item(remember { "${it.id}" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(
+                state.visibleKeys
+            ).isEqualTo(listOf(0, 1, 2))
+        }
+    }
+
+    @Test
+    fun keysInLazyListItemInfoAreCorrectAfterReordering() {
+        var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState()
+            LazyVerticalStaggeredGrid(columns = StaggeredGridCells.Fixed(columns), state = state) {
+                items(list, key = { it.id }) {
+                    Item(remember { "${it.id}" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = listOf(list[0], list[2], list[1])
+        }
+
+        rule.runOnIdle {
+            assertThat(
+                state.visibleKeys
+            ).isEqualTo(listOf(0, 2, 1))
+        }
+    }
+
+    @Test
+    fun addingItemsBeforeWithoutKeysIsMaintainingTheIndex() {
+        var list by mutableStateOf((10..15).toList())
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState()
+            LazyVerticalStaggeredGrid(
+                StaggeredGridCells.Fixed(columns),
+                Modifier.size(itemSize * 2.5f),
+                state
+            ) {
+                items(list) {
+                    Item(remember { "$it" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = (0..15).toList()
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun addingItemsBeforeKeepingThisItemFirst() {
+        var list by mutableStateOf((10..15).toList())
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState()
+            LazyVerticalStaggeredGrid(
+                StaggeredGridCells.Fixed(columns),
+                Modifier.size(itemSize * 2.5f),
+                state
+            ) {
+                items(list, key = { it }) {
+                    Item(remember { "$it" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = (0..15).toList()
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(10)
+            assertThat(
+                state.visibleKeys
+            ).isEqualTo(listOf(10, 11, 12, 13, 14, 15))
+        }
+    }
+
+    @Test
+    fun addingItemsRightAfterKeepingThisItemFirst() {
+        var list by mutableStateOf((0..5).toList() + (10..15).toList())
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState(5)
+            LazyVerticalStaggeredGrid(
+                StaggeredGridCells.Fixed(1),
+                Modifier.size(itemSize * 2.5f),
+                state
+            ) {
+                items(list, key = { it }) {
+                    Item(remember { "$it" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = (0..15).toList()
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(5)
+            assertThat(
+                state.visibleKeys
+            ).isEqualTo(listOf(5, 6, 7))
+        }
+    }
+
+    @Test
+    fun addingItemsBeforeWhileCurrentItemIsNotInTheBeginning() {
+        var list by mutableStateOf((10..30).toList())
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState(10) // key 20 is the first item
+            LazyVerticalStaggeredGrid(
+                StaggeredGridCells.Fixed(columns),
+                Modifier.size(itemSize * 2.5f),
+                state
+            ) {
+                items(list, key = { it }) {
+                    Item(remember { "$it" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = (0..30).toList()
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(20)
+            assertThat(
+                state.visibleKeys
+            ).isEqualTo(listOf(20, 21, 22, 23, 24, 25))
+        }
+    }
+
+    @Test
+    fun removingTheCurrentItemMaintainsTheIndex() {
+        var list by mutableStateOf((0..20).toList())
+        lateinit var state: LazyStaggeredGridState
+
+        rule.setContent {
+            state = rememberLazyStaggeredGridState(8)
+            LazyVerticalStaggeredGrid(
+                StaggeredGridCells.Fixed(columns),
+                Modifier.size(itemSize * 2.5f),
+                state
+            ) {
+                items(list, key = { it }) {
+                    Item(remember { "$it" })
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            list = (0..20) - 8
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(8)
+            assertThat(state.visibleKeys).isEqualTo(listOf(9, 10, 11, 12, 13, 14))
+        }
+    }
+
+    @Test
+    fun keysLambdaIsCalledOnlyOnce() {
+        var keyCalls = 0
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(1)) {
+                items(1, key = {
+                    keyCalls++
+                    0
+                }) {
+                    Item("item")
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(keyCalls).isEqualTo(1)
+        }
+    }
+
+    private fun testReordering(content: LazyStaggeredGridScope.(List<MyClass>) -> Unit) {
+        var list by mutableStateOf(listOf(MyClass(0), MyClass(1), MyClass(2)))
+
+        rule.setContent {
+            LazyVerticalStaggeredGrid(StaggeredGridCells.Fixed(columns)) {
+                content(list)
+            }
+        }
+
+        rule.runOnIdle {
+            list = listOf(list[0], list[2], list[1])
+        }
+
+        assertItems("0", "2", "1")
+    }
+
+    private fun assertItems(vararg tags: String) {
+        var currentTop = 0.dp
+        var column = 0
+        tags.forEach {
+            rule.onNodeWithTag(it)
+                .assertTopPositionInRootIsEqualTo(currentTop)
+                .assertHeightIsEqualTo(itemSize)
+            ++column
+            if (column == columns) {
+                currentTop += itemSize
+                column = 0
+            }
+        }
+    }
+
+    @Composable
+    private fun Item(tag: String) {
+        Spacer(
+            Modifier
+                .testTag(tag)
+                .size(itemSize)
+        )
+    }
+
+    private class MyClass(val id: Int)
+}
+
+val LazyStaggeredGridState.visibleKeys: List<Any> get() = layoutInfo.visibleItemsInfo.map { it.key }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
index 4dedd01..d5e13df 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
@@ -22,10 +22,12 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.list.setContentWithTestViewConfiguration
 import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
@@ -1934,4 +1936,60 @@
         rule.onNodeWithTag("1")
             .assertAxisBounds(DpOffset(itemSizeDp, itemSizeDp * 2), DpSize(itemSizeDp, itemSizeDp))
     }
+
+    @Test
+    fun scrollDuringMeasure() {
+        rule.setContent {
+            BoxWithConstraints {
+                val state = rememberLazyStaggeredGridState()
+                LazyStaggeredGrid(
+                    lanes = 1,
+                    state = state,
+                    modifier = Modifier.axisSize(
+                        crossAxis = itemSizeDp * 2,
+                        mainAxis = itemSizeDp * 5
+                    ),
+                ) {
+                    items(20) {
+                        Spacer(
+                            modifier = Modifier.mainAxisSize(itemSizeDp).testTag(it.toString())
+                        )
+                    }
+                }
+                LaunchedEffect(state) {
+                    state.scrollToItem(10)
+                }
+            }
+        }
+
+        rule.onNodeWithTag("10")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+    }
+
+    @Test
+    fun scrollInLaunchedEffect() {
+        rule.setContent {
+            val state = rememberLazyStaggeredGridState()
+            LazyStaggeredGrid(
+                lanes = 1,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp * 5
+                ),
+            ) {
+                items(20) {
+                    Spacer(
+                        modifier = Modifier.mainAxisSize(itemSizeDp).testTag(it.toString())
+                    )
+                }
+            }
+            LaunchedEffect(state) {
+                state.scrollToItem(10)
+            }
+        }
+
+        rule.onNodeWithTag("10")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCustomKeyTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCustomKeyTest.kt
new file mode 100644
index 0000000..5e3afed
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCustomKeyTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalFoundationApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class PagerCustomKeyTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun keysLambdaIsCalledOnlyOnce() {
+        var keyCalls = 0
+
+        rule.setContent {
+            HorizontalPager(
+                state = rememberPagerState { 1 },
+                key = {
+                    keyCalls++
+                    0
+                }
+            ) {
+                Spacer(Modifier.fillMaxSize())
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(keyCalls).isEqualTo(1)
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidMain/AndroidManifest.xml b/compose/foundation/foundation/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/compose/foundation/foundation/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt
index e5d86d8..077a75f 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt
@@ -20,8 +20,10 @@
 import androidx.compose.foundation.text.selection.TextFieldSelectionManager
 import androidx.compose.runtime.Composable
 
+// TODO (b/269341173) remove inline once these composables are non-trivial
+
 @Composable
-internal actual fun ContextMenuArea(
+internal actual inline fun ContextMenuArea(
     manager: TextFieldSelectionManager,
     content: @Composable () -> Unit
 ) {
@@ -29,7 +31,7 @@
 }
 
 @Composable
-internal actual fun ContextMenuArea(
+internal actual inline fun ContextMenuArea(
     manager: SelectionManager,
     content: @Composable () -> Unit
 ) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
index 748d6b1..7fb11a3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
@@ -143,6 +143,7 @@
     private var lastSize: Size? = null
     private var lastLayoutDirection: LayoutDirection? = null
     private var lastOutline: Outline? = null
+    private var lastShape: Shape? = null
 
     override fun ContentDrawScope.draw() {
         if (shape === RectangleShape) {
@@ -161,7 +162,7 @@
 
     private fun ContentDrawScope.drawOutline() {
         val outline =
-            if (size == lastSize && layoutDirection == lastLayoutDirection) {
+            if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
                 lastOutline!!
             } else {
                 shape.createOutline(size, layoutDirection, this)
@@ -171,5 +172,6 @@
         lastOutline = outline
         lastSize = size
         lastLayoutDirection = layoutDirection
+        lastShape = shape
     }
 }
\ No newline at end of file
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 61d3630..c96bdb3 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
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
@@ -201,8 +202,8 @@
     DrawModifierNode,
     FocusEventModifierNode {
 
-    private var contentWidth by mutableStateOf(0)
-    private var containerWidth by mutableStateOf(0)
+    private var contentWidth by mutableIntStateOf(0)
+    private var containerWidth by mutableIntStateOf(0)
     private var hasFocus by mutableStateOf(false)
     var spacing: MarqueeSpacing by mutableStateOf(spacing)
     var animationMode: MarqueeAnimationMode by mutableStateOf(animationMode)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Border.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Border.kt
index 0f0816b..e188941 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Border.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Border.kt
@@ -16,12 +16,10 @@
 
 package androidx.compose.foundation
 
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
 import androidx.compose.ui.draw.CacheDrawScope
 import androidx.compose.ui.draw.DrawResult
-import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.draw.CacheDrawModifierNode
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.RoundRect
@@ -47,8 +45,9 @@
 import androidx.compose.ui.graphics.drawscope.clipRect
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.graphics.drawscope.translate
-import androidx.compose.ui.node.Ref
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.toSize
@@ -85,68 +84,29 @@
  * and clip it.
  *
  * @sample androidx.compose.foundation.samples.BorderSampleWithBrush()
+ * @sample androidx.compose.foundation.samples.BorderSampleWithDynamicData()
  *
  * @param width width of the border. Use [Dp.Hairline] for a hairline border.
  * @param brush brush to paint the border with
  * @param shape shape of the border
  */
-fun Modifier.border(width: Dp, brush: Brush, shape: Shape): Modifier = composed(
-    factory = {
-        // BorderCache object that is lazily allocated depending on the type of shape
-        // This object is only used for generic shapes and rounded rectangles with different corner
-        // radius sizes.
-        val borderCacheRef = remember { Ref<BorderCache>() }
-        this.then(
-            Modifier.drawWithCache {
-                val hasValidBorderParams = width.toPx() >= 0f && size.minDimension > 0f
-                if (!hasValidBorderParams) {
-                    drawContentWithoutBorder()
-                } else {
-                    val strokeWidthPx = min(
-                        if (width == Dp.Hairline) 1f else ceil(width.toPx()),
-                        ceil(size.minDimension / 2)
-                    )
-                    val halfStroke = strokeWidthPx / 2
-                    val topLeft = Offset(halfStroke, halfStroke)
-                    val borderSize = Size(
-                        size.width - strokeWidthPx,
-                        size.height - strokeWidthPx
-                    )
-                    // The stroke is larger than the drawing area so just draw a full shape instead
-                    val fillArea = (strokeWidthPx * 2) > size.minDimension
-                    when (val outline = shape.createOutline(size, layoutDirection, this)) {
-                        is Outline.Generic ->
-                            drawGenericBorder(
-                                borderCacheRef,
-                                brush,
-                                outline,
-                                fillArea,
-                                strokeWidthPx
-                            )
-                        is Outline.Rounded ->
-                            drawRoundRectBorder(
-                                borderCacheRef,
-                                brush,
-                                outline,
-                                topLeft,
-                                borderSize,
-                                fillArea,
-                                strokeWidthPx
-                            )
-                        is Outline.Rectangle ->
-                            drawRectBorder(
-                                brush,
-                                topLeft,
-                                borderSize,
-                                fillArea,
-                                strokeWidthPx
-                            )
-                    }
-                }
-            }
-        )
-    },
-    inspectorInfo = debugInspectorInfo {
+fun Modifier.border(width: Dp, brush: Brush, shape: Shape) =
+    this then BorderModifierNodeElement(width, brush, shape)
+
+internal data class BorderModifierNodeElement(
+    val width: Dp,
+    val brush: Brush,
+    val shape: Shape
+) : ModifierNodeElement<BorderModifierNode>() {
+    override fun create() = BorderModifierNode(width, brush, shape)
+
+    override fun update(node: BorderModifierNode) {
+        node.width = width
+        node.brush = brush
+        node.shape = shape
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
         name = "border"
         properties["width"] = width
         if (brush is SolidColor) {
@@ -157,10 +117,241 @@
         }
         properties["shape"] = shape
     }
-)
+}
 
-private fun Ref<BorderCache>.obtain(): BorderCache =
-    this.value ?: BorderCache().also { value = it }
+internal class BorderModifierNode(
+    widthParameter: Dp,
+    brushParameter: Brush,
+    shapeParameter: Shape
+) : DelegatingNode() {
+
+    // BorderCache object that is lazily allocated depending on the type of shape
+    // This object is only used for generic shapes and rounded rectangles with different corner
+    // radius sizes.
+    // Note: Extension functions that use BorderCache are part of this class.
+    private var borderCache: BorderCache? = null
+
+    var width = widthParameter
+        set(value) {
+            field = value
+            drawWithCacheModifierNode.invalidateDrawCache()
+        }
+    var brush = brushParameter
+    var shape = shapeParameter
+        set(value) {
+            field = value
+            drawWithCacheModifierNode.invalidateDrawCache()
+        }
+
+    private val drawWithCacheModifierNode = delegate(
+        CacheDrawModifierNode {
+            val hasValidBorderParams = width.toPx() >= 0f && size.minDimension > 0f
+            if (!hasValidBorderParams) {
+                drawContentWithoutBorder()
+            } else {
+                val strokeWidthPx = min(
+                    if (width == Dp.Hairline) 1f else ceil(width.toPx()),
+                    ceil(size.minDimension / 2)
+                )
+                val halfStroke = strokeWidthPx / 2
+                val topLeft = Offset(halfStroke, halfStroke)
+                val borderSize = Size(
+                    size.width - strokeWidthPx,
+                    size.height - strokeWidthPx
+                )
+                // The stroke is larger than the drawing area so just draw a full shape instead
+                val fillArea = (strokeWidthPx * 2) > size.minDimension
+                when (val outline = shape.createOutline(size, layoutDirection, this)) {
+                    is Outline.Generic ->
+                        drawGenericBorder(
+                            brush,
+                            outline,
+                            fillArea,
+                            strokeWidthPx
+                        )
+
+                    is Outline.Rounded ->
+                        drawRoundRectBorder(
+                            brush,
+                            outline,
+                            topLeft,
+                            borderSize,
+                            fillArea,
+                            strokeWidthPx
+                        )
+
+                    is Outline.Rectangle ->
+                        drawRectBorder(
+                            brush,
+                            topLeft,
+                            borderSize,
+                            fillArea,
+                            strokeWidthPx
+                        )
+                }
+            }
+        }
+    )
+
+    /**
+     * Border implementation for generic paths. Note it is possible to be given paths
+     * that do not make sense in the context of a border (ex. a figure 8 path or a non-enclosed
+     * shape) We do not handle that here as we expect developers to give us enclosed,
+     * non-overlapping paths.
+     */
+    private fun CacheDrawScope.drawGenericBorder(
+        brush: Brush,
+        outline: Outline.Generic,
+        fillArea: Boolean,
+        strokeWidth: Float
+    ): DrawResult =
+        if (fillArea) {
+            onDrawWithContent {
+                drawContent()
+                drawPath(outline.path, brush = brush)
+            }
+        } else {
+            // Optimization, if we are only drawing a solid color border, we only need an alpha8
+            // mask as we can draw the mask with a tint.
+            // Otherwise we need to allocate a full ImageBitmap and draw it normally
+            val config: ImageBitmapConfig
+            val colorFilter: ColorFilter?
+            if (brush is SolidColor) {
+                config = ImageBitmapConfig.Alpha8
+                colorFilter = ColorFilter.tint(brush.value)
+            } else {
+                config = ImageBitmapConfig.Argb8888
+                colorFilter = null
+            }
+
+            val pathBounds = outline.path.getBounds()
+            // Create a mask path that includes a rectangle with the original path cut out of it.
+            // Note: borderCache is part of the class that defines this extension function.
+            if (borderCache == null) {
+                borderCache = BorderCache()
+            }
+            val maskPath = borderCache!!.obtainPath().apply {
+                reset()
+                addRect(pathBounds)
+                op(this, outline.path, PathOperation.Difference)
+            }
+
+            val cacheImageBitmap: ImageBitmap
+            val pathBoundsSize = IntSize(
+                ceil(pathBounds.width).toInt(),
+                ceil(pathBounds.height).toInt()
+            )
+            with(borderCache!!) {
+                // Draw into offscreen bitmap with the size of the path
+                // We need to draw into this intermediate bitmap to act as a layer
+                // and make sure that the clearing logic does not generate underdraw
+                // into the target we are rendering into
+                cacheImageBitmap = drawBorderCache(
+                    pathBoundsSize,
+                    config
+                ) {
+                    // Paths can have offsets, so translate to keep the drawn path
+                    // within the bounds of the mask bitmap
+                    translate(-pathBounds.left, -pathBounds.top) {
+                        // Draw the path with a stroke width twice the provided value.
+                        // Because strokes are centered, this will draw both and inner and
+                        // outer stroke with the desired stroke width
+                        drawPath(
+                            path = outline.path,
+                            brush = brush,
+                            style = Stroke(strokeWidth * 2)
+                        )
+
+                        // Scale the canvas slightly to cover the background that may be visible
+                        // after clearing the outer stroke
+                        scale(
+                            (size.width + 1) / size.width,
+                            (size.height + 1) / size.height
+                        ) {
+                            // Remove the outer stroke by clearing the inverted mask path
+                            drawPath(path = maskPath, brush = brush, blendMode = BlendMode.Clear)
+                        }
+                    }
+                }
+            }
+
+            onDrawWithContent {
+                drawContent()
+                translate(pathBounds.left, pathBounds.top) {
+                    drawImage(cacheImageBitmap, srcSize = pathBoundsSize, colorFilter = colorFilter)
+                }
+            }
+        }
+
+    /**
+     * Border implementation for simple rounded rects and those with different corner
+     * radii
+     */
+    private fun CacheDrawScope.drawRoundRectBorder(
+        brush: Brush,
+        outline: Outline.Rounded,
+        topLeft: Offset,
+        borderSize: Size,
+        fillArea: Boolean,
+        strokeWidth: Float
+    ): DrawResult {
+        return if (outline.roundRect.isSimple) {
+            val cornerRadius = outline.roundRect.topLeftCornerRadius
+            val halfStroke = strokeWidth / 2
+            val borderStroke = Stroke(strokeWidth)
+            onDrawWithContent {
+                drawContent()
+                when {
+                    fillArea -> {
+                        // If the drawing area is smaller than the stroke being drawn
+                        // drawn all around it just draw a filled in rounded rect
+                        drawRoundRect(brush, cornerRadius = cornerRadius)
+                    }
+                    cornerRadius.x < halfStroke -> {
+                        // If the corner radius is smaller than half of the stroke width
+                        // then the interior curvature of the stroke will be a sharp edge
+                        // In this case just draw a normal filled in rounded rect with the
+                        // desired corner radius but clipping out the interior rectangle
+                        clipRect(
+                            strokeWidth,
+                            strokeWidth,
+                            size.width - strokeWidth,
+                            size.height - strokeWidth,
+                            clipOp = ClipOp.Difference
+                        ) {
+                            drawRoundRect(brush, cornerRadius = cornerRadius)
+                        }
+                    }
+                    else -> {
+                        // Otherwise draw a stroked rounded rect with the corner radius
+                        // shrunk by half of the stroke width. This will ensure that the
+                        // outer curvature of the rounded rectangle will have the desired
+                        // corner radius.
+                        drawRoundRect(
+                            brush = brush,
+                            topLeft = topLeft,
+                            size = borderSize,
+                            cornerRadius = cornerRadius.shrink(halfStroke),
+                            style = borderStroke
+                        )
+                    }
+                }
+            }
+        } else {
+            // Note: borderCache is part of the class that defines this extension function.
+            if (borderCache == null) {
+                borderCache = BorderCache()
+            }
+            val path = borderCache!!.obtainPath()
+            val roundedRectPath =
+                createRoundRectPath(path, outline.roundRect, strokeWidth, fillArea)
+            onDrawWithContent {
+                drawContent()
+                drawPath(roundedRectPath, brush = brush)
+            }
+        }
+    }
+}
 
 /**
  * Helper object that handles lazily allocating and re-using objects
@@ -202,7 +393,8 @@
             }
         }
 
-        val targetDrawScope = canvasDrawScope ?: CanvasDrawScope().also { canvasDrawScope = it }
+        val targetDrawScope =
+            canvasDrawScope ?: CanvasDrawScope().also { canvasDrawScope = it }
         val drawSize = borderSize.toSize()
         targetDrawScope.draw(
             this,
@@ -237,155 +429,6 @@
     }
 
 /**
- * Border implementation for generic paths. Note it is possible to be given paths
- * that do not make sense in the context of a border (ex. a figure 8 path or a non-enclosed
- * shape) We do not handle that here as we expect developers to give us enclosed, non-overlapping
- * paths
- */
-private fun CacheDrawScope.drawGenericBorder(
-    borderCacheRef: Ref<BorderCache>,
-    brush: Brush,
-    outline: Outline.Generic,
-    fillArea: Boolean,
-    strokeWidth: Float
-): DrawResult =
-    if (fillArea) {
-        onDrawWithContent {
-            drawContent()
-            drawPath(outline.path, brush = brush)
-        }
-    } else {
-        // Optimization, if we are only drawing a solid color border, we only need an alpha8 mask
-        // as we can draw the mask with a tint.
-        // Otherwise we need to allocate a full ImageBitmap and draw it normally
-        val config: ImageBitmapConfig
-        val colorFilter: ColorFilter?
-        if (brush is SolidColor) {
-            config = ImageBitmapConfig.Alpha8
-            colorFilter = ColorFilter.tint(brush.value)
-        } else {
-            config = ImageBitmapConfig.Argb8888
-            colorFilter = null
-        }
-
-        val pathBounds = outline.path.getBounds()
-        val borderCache = borderCacheRef.obtain()
-        // Create a mask path that includes a rectangle with the original path cut out of it
-        val maskPath = borderCache.obtainPath().apply {
-            reset()
-            addRect(pathBounds)
-            op(this, outline.path, PathOperation.Difference)
-        }
-
-        val cacheImageBitmap: ImageBitmap
-        val pathBoundsSize = IntSize(
-            ceil(pathBounds.width).toInt(),
-            ceil(pathBounds.height).toInt()
-        )
-        with(borderCache) {
-            // Draw into offscreen bitmap with the size of the path
-            // We need to draw into this intermediate bitmap to act as a layer
-            // and make sure that the clearing logic does not generate underdraw
-            // into the target we are rendering into
-            cacheImageBitmap = drawBorderCache(
-                pathBoundsSize,
-                config
-            ) {
-                // Paths can have offsets, so translate to keep the drawn path
-                // within the bounds of the mask bitmap
-                translate(-pathBounds.left, -pathBounds.top) {
-                    // Draw the path with a stroke width twice the provided value.
-                    // Because strokes are centered, this will draw both and inner and outer stroke
-                    // with the desired stroke width
-                    drawPath(path = outline.path, brush = brush, style = Stroke(strokeWidth * 2))
-
-                    // Scale the canvas slightly to cover the background that may be visible
-                    // after clearing the outer stroke
-                    scale(
-                        (size.width + 1) / size.width,
-                        (size.height + 1) / size.height
-                    ) {
-                        // Remove the outer stroke by clearing the inverted mask path
-                        drawPath(path = maskPath, brush = brush, blendMode = BlendMode.Clear)
-                    }
-                }
-            }
-        }
-
-        onDrawWithContent {
-            drawContent()
-            translate(pathBounds.left, pathBounds.top) {
-                drawImage(cacheImageBitmap, srcSize = pathBoundsSize, colorFilter = colorFilter)
-            }
-        }
-    }
-
-/**
- * Border implementation for simple rounded rects and those with different corner
- * radii
- */
-private fun CacheDrawScope.drawRoundRectBorder(
-    borderCacheRef: Ref<BorderCache>,
-    brush: Brush,
-    outline: Outline.Rounded,
-    topLeft: Offset,
-    borderSize: Size,
-    fillArea: Boolean,
-    strokeWidth: Float
-): DrawResult {
-    return if (outline.roundRect.isSimple) {
-        val cornerRadius = outline.roundRect.topLeftCornerRadius
-        val halfStroke = strokeWidth / 2
-        val borderStroke = Stroke(strokeWidth)
-        onDrawWithContent {
-            drawContent()
-            when {
-                fillArea -> {
-                    // If the drawing area is smaller than the stroke being drawn
-                    // drawn all around it just draw a filled in rounded rect
-                    drawRoundRect(brush, cornerRadius = cornerRadius)
-                }
-                cornerRadius.x < halfStroke -> {
-                    // If the corner radius is smaller than half of the stroke width
-                    // then the interior curvature of the stroke will be a sharp edge
-                    // In this case just draw a normal filled in rounded rect with the
-                    // desired corner radius but clipping out the interior rectangle
-                    clipRect(
-                        strokeWidth,
-                        strokeWidth,
-                        size.width - strokeWidth,
-                        size.height - strokeWidth,
-                        clipOp = ClipOp.Difference
-                    ) {
-                        drawRoundRect(brush, cornerRadius = cornerRadius)
-                    }
-                }
-                else -> {
-                    // Otherwise draw a stroked rounded rect with the corner radius
-                    // shrunk by half of the stroke width. This will ensure that the
-                    // outer curvature of the rounded rectangle will have the desired
-                    // corner radius.
-                    drawRoundRect(
-                        brush = brush,
-                        topLeft = topLeft,
-                        size = borderSize,
-                        cornerRadius = cornerRadius.shrink(halfStroke),
-                        style = borderStroke
-                    )
-                }
-            }
-        }
-    } else {
-        val path = borderCacheRef.obtain().obtainPath()
-        val roundedRectPath = createRoundRectPath(path, outline.roundRect, strokeWidth, fillArea)
-        onDrawWithContent {
-            drawContent()
-            drawPath(roundedRectPath, brush = brush)
-        }
-    }
-}
-
-/**
  * Border implementation for rectangular borders
  */
 private fun CacheDrawScope.drawRectBorder(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index 0ce2c64..4eefc4c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -33,7 +33,6 @@
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.saveable.Saver
@@ -96,7 +95,7 @@
     /**
      * current scroll position value in pixels
      */
-    var value: Int by mutableStateOf(initial)
+    var value: Int by mutableIntStateOf(initial)
         private set
 
     /**
@@ -114,7 +113,7 @@
     /**
      * Size of the viewport on the scrollable axis, or 0 if still unknown.
      */
-    internal var viewportSize: Int by mutableStateOf(0)
+    internal var viewportSize: Int by mutableIntStateOf(0)
 
     /**
      * [InteractionSource] that will be used to dispatch drag events when this
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/DataIndex.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/DataIndex.kt
deleted file mode 100644
index 8a98269..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/DataIndex.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.lazy
-
-/**
- * Represents an index in the list of items of lazy layout.
- */
-@Suppress("NOTHING_TO_INLINE")
-@kotlin.jvm.JvmInline
-internal value class DataIndex(val value: Int) {
-    inline operator fun inc(): DataIndex = DataIndex(value + 1)
-    inline operator fun dec(): DataIndex = DataIndex(value - 1)
-    inline operator fun plus(i: Int): DataIndex = DataIndex(value + i)
-    inline operator fun minus(i: Int): DataIndex = DataIndex(value - i)
-    inline operator fun minus(i: DataIndex): DataIndex = DataIndex(value - i.value)
-    inline operator fun compareTo(other: DataIndex): Int = value - other.value
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 4994b52..05949c7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -262,9 +262,9 @@
         ) { index, key, contentType, placeables ->
             // we add spaceBetweenItems as an extra spacing for all items apart from the last one so
             // the lazy list measuring logic will take it into account.
-            val spacing = if (index.value == itemsCount - 1) 0 else spaceBetweenItems
+            val spacing = if (index == itemsCount - 1) 0 else spaceBetweenItems
             LazyListMeasuredItem(
-                index = index.value,
+                index = index,
                 placeables = placeables,
                 isVertical = isVertical,
                 horizontalAlignment = horizontalAlignment,
@@ -281,10 +281,10 @@
         }
         state.premeasureConstraints = measuredItemProvider.childConstraints
 
-        val firstVisibleItemIndex: DataIndex
+        val firstVisibleItemIndex: Int
         val firstVisibleScrollOffset: Int
         Snapshot.withoutReadObservation {
-            firstVisibleItemIndex = DataIndex(state.firstVisibleItemIndex)
+            firstVisibleItemIndex = state.firstVisibleItemIndex
             firstVisibleScrollOffset = state.firstVisibleItemScrollOffset
         }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
index 2631561..978d3a8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
@@ -70,7 +70,7 @@
         return null
     }
 
-    val measuredHeaderItem = itemProvider.getAndMeasure(DataIndex(currentHeaderListPosition))
+    val measuredHeaderItem = itemProvider.getAndMeasure(currentHeaderListPosition)
 
     var headerOffset = if (currentHeaderOffset != Int.MIN_VALUE) {
         maxOf(-beforeContentPadding, currentHeaderOffset)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
index 15ebac4..497ba6d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
@@ -33,7 +33,7 @@
     private val activeKeys = mutableSetOf<Any>()
 
     // snapshot of the key to index map used for the last measuring.
-    private var keyToIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap.Empty
+    private var keyIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap.Empty
 
     // keeps the index of the first visible item index.
     private var firstVisibleIndex = 0
@@ -66,8 +66,8 @@
 
         val previousFirstVisibleIndex = firstVisibleIndex
         firstVisibleIndex = positionedItems.firstOrNull()?.index ?: 0
-        val previousKeyToIndexMap = keyToIndexMap
-        keyToIndexMap = itemProvider.keyToIndexMap
+        val previousKeyToIndexMap = keyIndexMap
+        keyIndexMap = itemProvider.keyIndexMap
 
         val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
 
@@ -87,7 +87,7 @@
             if (item.hasAnimations) {
                 if (!activeKeys.contains(item.key)) {
                     activeKeys += item.key
-                    val previousIndex = previousKeyToIndexMap[item.key]
+                    val previousIndex = previousKeyToIndexMap.getIndex(item.key)
                     if (previousIndex != -1 && item.index != previousIndex) {
                         if (previousIndex < previousFirstVisibleIndex) {
                             // the larger index will be in the start of the list
@@ -116,7 +116,7 @@
         }
 
         var accumulatedOffset = 0
-        movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
+        movingInFromStartBound.sortByDescending { previousKeyToIndexMap.getIndex(it.key) }
         movingInFromStartBound.fastForEach { item ->
             accumulatedOffset += item.size
             val mainAxisOffset = 0 - accumulatedOffset
@@ -124,7 +124,7 @@
             startAnimationsIfNeeded(item)
         }
         accumulatedOffset = 0
-        movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
+        movingInFromEndBound.sortBy { previousKeyToIndexMap.getIndex(it.key) }
         movingInFromEndBound.fastForEach { item ->
             val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
             accumulatedOffset += item.size
@@ -135,12 +135,12 @@
         movingAwayKeys.forEach { key ->
             // found an item which was in our map previously but is not a part of the
             // positionedItems now
-            val newIndex = keyToIndexMap[key]
+            val newIndex = keyIndexMap.getIndex(key)
 
             if (newIndex == -1) {
                 activeKeys.remove(key)
             } else {
-                val item = itemProvider.getAndMeasure(DataIndex(newIndex))
+                val item = itemProvider.getAndMeasure(newIndex)
                 // check if we have any active placement animation on the item
                 var inProgress = false
                 repeat(item.placeablesCount) {
@@ -149,7 +149,7 @@
                         return@repeat
                     }
                 }
-                if ((!inProgress && newIndex == previousKeyToIndexMap[key])) {
+                if ((!inProgress && newIndex == previousKeyToIndexMap.getIndex(key))) {
                     activeKeys.remove(key)
                 } else {
                     if (newIndex < firstVisibleIndex) {
@@ -162,7 +162,7 @@
         }
 
         accumulatedOffset = 0
-        movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
+        movingAwayToStartBound.sortByDescending { keyIndexMap.getIndex(it.key) }
         movingAwayToStartBound.fastForEach { item ->
             accumulatedOffset += item.size
             val mainAxisOffset = 0 - accumulatedOffset
@@ -172,7 +172,7 @@
             startAnimationsIfNeeded(positionedItem)
         }
         accumulatedOffset = 0
-        movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
+        movingAwayToEndBound.sortBy { keyIndexMap.getIndex(it.key) }
         movingAwayToEndBound.fastForEach { item ->
             val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
             accumulatedOffset += item.size
@@ -195,7 +195,7 @@
      */
     fun reset() {
         activeKeys.clear()
-        keyToIndexMap = LazyLayoutKeyIndexMap.Empty
+        keyIndexMap = LazyLayoutKeyIndexMap.Empty
         firstVisibleIndex = -1
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt
index 31cbd85..1a5eb66 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
-import androidx.compose.foundation.lazy.layout.PinnableItem
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnableItem
 import androidx.compose.foundation.lazy.layout.NearestRangeKeyIndexMapState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
@@ -30,7 +30,7 @@
 
 @ExperimentalFoundationApi
 internal interface LazyListItemProvider : LazyLayoutItemProvider {
-    val keyToIndexMap: LazyLayoutKeyIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap
     /** The list of indexes of the sticky header items */
     val headerIndexes: List<Int>
     /** The scope used by the item content lambdas */
@@ -66,26 +66,28 @@
     override val itemCount: Int get() = listContent.itemCount
 
     @Composable
-    override fun Item(index: Int) {
-        listContent.PinnableItem(index, state.pinnedItems) { localIndex ->
-            with(itemScope) { item(localIndex) }
+    override fun Item(index: Int, key: Any) {
+        LazyLayoutPinnableItem(key, index, state.pinnedItems) {
+            listContent.withInterval(index) { localIndex, content ->
+                content.item(itemScope, localIndex)
+            }
         }
     }
 
-    override fun getKey(index: Int): Any = listContent.getKey(index)
+    override fun getKey(index: Int): Any = keyIndexMap.getKey(index) ?: listContent.getKey(index)
 
     override fun getContentType(index: Int): Any? = listContent.getContentType(index)
 
     override val headerIndexes: List<Int> get() = listContent.headerIndexes
 
-    override val keyToIndexMap by NearestRangeKeyIndexMapState(
+    override val keyIndexMap by NearestRangeKeyIndexMapState(
         firstVisibleItemIndex = { state.firstVisibleItemIndex },
         slidingWindowSize = { NearestItemsSlidingWindowSize },
         extraItemCount = { NearestItemsExtraItemCount },
         content = { listContent }
     )
 
-    override fun getIndex(key: Any): Int = keyToIndexMap[key]
+    override fun getIndex(key: Any): Int = keyIndexMap.getIndex(key)
 }
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
index 0c97ea2..91d0350 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
@@ -44,7 +44,7 @@
     beforeContentPadding: Int,
     afterContentPadding: Int,
     spaceBetweenItems: Int,
-    firstVisibleItemIndex: DataIndex,
+    firstVisibleItemIndex: Int,
     firstVisibleItemScrollOffset: Int,
     scrollToBeConsumed: Float,
     constraints: Constraints,
@@ -81,10 +81,10 @@
     } else {
         var currentFirstItemIndex = firstVisibleItemIndex
         var currentFirstItemScrollOffset = firstVisibleItemScrollOffset
-        if (currentFirstItemIndex.value >= itemsCount) {
+        if (currentFirstItemIndex >= itemsCount) {
             // the data set has been updated and now we have less items that we were
             // scrolled to before
-            currentFirstItemIndex = DataIndex(itemsCount - 1)
+            currentFirstItemIndex = itemsCount - 1
             currentFirstItemScrollOffset = 0
         }
 
@@ -96,7 +96,7 @@
         currentFirstItemScrollOffset -= scrollDelta
 
         // if the current scroll offset is less than minimally possible
-        if (currentFirstItemIndex == DataIndex(0) && currentFirstItemScrollOffset < 0) {
+        if (currentFirstItemIndex == 0 && currentFirstItemScrollOffset < 0) {
             scrollDelta += currentFirstItemScrollOffset
             currentFirstItemScrollOffset = 0
         }
@@ -119,8 +119,8 @@
         // we had scrolled backward or we compose items in the start padding area, which means
         // items before current firstItemScrollOffset should be visible. compose them and update
         // firstItemScrollOffset
-        while (currentFirstItemScrollOffset < 0 && currentFirstItemIndex > DataIndex(0)) {
-            val previous = DataIndex(currentFirstItemIndex.value - 1)
+        while (currentFirstItemScrollOffset < 0 && currentFirstItemIndex > 0) {
+            val previous = currentFirstItemIndex - 1
             val measuredItem = measuredItemProvider.getAndMeasure(previous)
             visibleItems.add(0, measuredItem)
             maxCrossAxis = maxOf(maxCrossAxis, measuredItem.crossAxisSize)
@@ -151,7 +151,7 @@
         // then composing visible items forward until we fill the whole viewport.
         // we want to have at least one item in visibleItems even if in fact all the items are
         // offscreen, this can happen if the content padding is larger than the available size.
-        while (index.value < itemsCount &&
+        while (index < itemsCount &&
             (currentMainAxisOffset < maxMainAxis ||
                 currentMainAxisOffset <= 0 || // filling beforeContentPadding area
                 visibleItems.isEmpty())
@@ -159,7 +159,7 @@
             val measuredItem = measuredItemProvider.getAndMeasure(index)
             currentMainAxisOffset += measuredItem.sizeWithSpacings
 
-            if (currentMainAxisOffset <= minOffset && index.value != itemsCount - 1) {
+            if (currentMainAxisOffset <= minOffset && index != itemsCount - 1) {
                 // this item is offscreen and will not be placed. advance firstVisibleItemIndex
                 currentFirstItemIndex = index + 1
                 currentFirstItemScrollOffset -= measuredItem.sizeWithSpacings
@@ -178,9 +178,9 @@
             currentFirstItemScrollOffset -= toScrollBack
             currentMainAxisOffset += toScrollBack
             while (currentFirstItemScrollOffset < beforeContentPadding &&
-                currentFirstItemIndex > DataIndex(0)
+                currentFirstItemIndex > 0
             ) {
-                val previousIndex = DataIndex(currentFirstItemIndex.value - 1)
+                val previousIndex = currentFirstItemIndex - 1
                 val measuredItem = measuredItemProvider.getAndMeasure(previousIndex)
                 visibleItems.add(0, measuredItem)
                 maxCrossAxis = maxOf(maxCrossAxis, measuredItem.crossAxisSize)
@@ -305,7 +305,7 @@
         return LazyListMeasureResult(
             firstVisibleItem = firstItem,
             firstVisibleItemScrollOffset = currentFirstItemScrollOffset,
-            canScrollForward = index.value < itemsCount || currentMainAxisOffset > maxOffset,
+            canScrollForward = index < itemsCount || currentMainAxisOffset > maxOffset,
             consumedScroll = consumedScroll,
             measureResult = layout(layoutWidth, layoutHeight) {
                 positionedItems.fastForEach {
@@ -345,7 +345,7 @@
     fun addItem(index: Int) {
         if (list == null) list = mutableListOf()
         requireNotNull(list).add(
-            measuredItemProvider.getAndMeasure(DataIndex(index))
+            measuredItemProvider.getAndMeasure(index)
         )
     }
 
@@ -365,25 +365,25 @@
 }
 
 private fun createItemsBeforeList(
-    currentFirstItemIndex: DataIndex,
+    currentFirstItemIndex: Int,
     measuredItemProvider: LazyListMeasuredItemProvider,
     beyondBoundsItemCount: Int,
     pinnedItems: List<Int>
 ): List<LazyListMeasuredItem> {
     var list: MutableList<LazyListMeasuredItem>? = null
 
-    var start = currentFirstItemIndex.value
+    var start = currentFirstItemIndex
 
     fun addItem(index: Int) {
         if (list == null) list = mutableListOf()
         requireNotNull(list).add(
-            measuredItemProvider.getAndMeasure(DataIndex(index))
+            measuredItemProvider.getAndMeasure(index)
         )
     }
 
     start = maxOf(0, start - beyondBoundsItemCount)
 
-    for (i in currentFirstItemIndex.value - 1 downTo start) {
+    for (i in currentFirstItemIndex - 1 downTo start) {
         addItem(i)
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt
index 38baf57..77ec076 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt
@@ -43,10 +43,10 @@
      * Used to subcompose items of lazy lists. Composed placeables will be measured with the
      * correct constraints and wrapped into [LazyListMeasuredItem].
      */
-    fun getAndMeasure(index: DataIndex): LazyListMeasuredItem {
-        val key = itemProvider.getKey(index.value)
-        val contentType = itemProvider.getContentType(index.value)
-        val placeables = measureScope.measure(index.value, childConstraints)
+    fun getAndMeasure(index: Int): LazyListMeasuredItem {
+        val key = keyIndexMap.getKey(index) ?: itemProvider.getKey(index)
+        val contentType = itemProvider.getContentType(index)
+        val placeables = measureScope.measure(index, childConstraints)
         return measuredItemFactory.createItem(index, key, contentType, placeables)
     }
 
@@ -54,13 +54,13 @@
      * Contains the mapping between the key and the index. It could contain not all the items of
      * the list as an optimization.
      **/
-    val keyToIndexMap: LazyLayoutKeyIndexMap get() = itemProvider.keyToIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap = itemProvider.keyIndexMap
 }
 
 // This interface allows to avoid autoboxing on index param
 internal fun interface MeasuredItemFactory {
     fun createItem(
-        index: DataIndex,
+        index: Int,
         key: Any,
         contentType: Any?,
         placeables: List<Placeable>
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
index 4717a84..16eb1c7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.findIndexByKey
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
 
@@ -31,9 +31,9 @@
     initialIndex: Int = 0,
     initialScrollOffset: Int = 0
 ) {
-    var index by mutableStateOf(DataIndex(initialIndex))
+    var index by mutableIntStateOf(initialIndex)
 
-    var scrollOffset by mutableStateOf(initialScrollOffset)
+    var scrollOffset by mutableIntStateOf(initialScrollOffset)
         private set
 
     private var hadFirstNotEmptyLayout = false
@@ -56,7 +56,7 @@
 
             Snapshot.withoutReadObservation {
                 update(
-                    DataIndex(measureResult.firstVisibleItem?.index ?: 0),
+                    measureResult.firstVisibleItem?.index ?: 0,
                     scrollOffset
                 )
             }
@@ -74,7 +74,7 @@
      * c) there will be not enough items to fill the viewport after the requested index, so we
      * would have to compose few elements before the asked index, changing the first visible item.
      */
-    fun requestPosition(index: DataIndex, scrollOffset: Int) {
+    fun requestPosition(index: Int, scrollOffset: Int) {
         update(index, scrollOffset)
         // clear the stored key as we have a direct request to scroll to [index] position and the
         // next [checkIfFirstVisibleItemWasMoved] shouldn't override this.
@@ -91,14 +91,14 @@
     fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyListItemProvider) {
         Snapshot.withoutReadObservation {
             update(
-                DataIndex(itemProvider.findIndexByKey(lastKnownFirstItemKey, index.value)),
+                itemProvider.findIndexByKey(lastKnownFirstItemKey, index),
                 scrollOffset
             )
         }
     }
 
-    private fun update(index: DataIndex, scrollOffset: Int) {
-        require(index.value >= 0f) { "Index should be non-negative (${index.value})" }
+    private fun update(index: Int, scrollOffset: Int) {
+        require(index >= 0f) { "Index should be non-negative ($index)" }
         if (index != this.index) {
             this.index = index
         }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 760f9fa..3bd36be 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -23,9 +23,10 @@
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.lazy.layout.AwaitFirstLayoutModifier
 import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
-import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
+import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.foundation.lazy.layout.animateScrollToItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
@@ -35,16 +36,11 @@
 import androidx.compose.runtime.saveable.listSaver
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.OnGloballyPositionedModifier
 import androidx.compose.ui.layout.Remeasurement
 import androidx.compose.ui.layout.RemeasurementModifier
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
 import kotlin.math.abs
 
 /**
@@ -107,7 +103,7 @@
      * derived state in order to only have recompositions when the derived value changes:
      * @sample androidx.compose.foundation.samples.UsingListScrollPositionInCompositionSample
      */
-    val firstVisibleItemIndex: Int get() = scrollPosition.index.value
+    val firstVisibleItemIndex: Int get() = scrollPosition.index
 
     /**
      * The scroll offset of the first visible item. Scrolling forward is positive - i.e., the
@@ -249,7 +245,7 @@
     }
 
     internal fun snapToItemIndexInternal(index: Int, scrollOffset: Int) {
-        scrollPosition.requestPosition(DataIndex(index), scrollOffset)
+        scrollPosition.requestPosition(index, scrollOffset)
         // placement animation is not needed because we snap into a new position.
         placementAnimator.reset()
         remeasurement?.forceRemeasure()
@@ -436,25 +432,4 @@
     override val beforeContentPadding = 0
     override val afterContentPadding = 0
     override val mainAxisItemSpacing = 0
-}
-
-internal class AwaitFirstLayoutModifier : OnGloballyPositionedModifier {
-    private var wasPositioned = false
-    private var continuation: Continuation<Unit>? = null
-
-    suspend fun waitForFirstLayout() {
-        if (!wasPositioned) {
-            val oldContinuation = continuation
-            suspendCoroutine<Unit> { continuation = it }
-            oldContinuation?.resume(Unit)
-        }
-    }
-
-    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
-        if (!wasPositioned) {
-            wasPositioned = true
-            continuation?.resume(Unit)
-            continuation = null
-        }
-    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/ItemIndex.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/ItemIndex.kt
deleted file mode 100644
index 7207064..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/ItemIndex.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.lazy.grid
-
-/**
- * Represents a line index in the lazy grid.
- */
-@Suppress("NOTHING_TO_INLINE")
-@kotlin.jvm.JvmInline
-internal value class LineIndex(val value: Int) {
-    inline operator fun inc(): LineIndex = LineIndex(value + 1)
-    inline operator fun dec(): LineIndex = LineIndex(value - 1)
-    inline operator fun plus(i: Int): LineIndex = LineIndex(value + i)
-    inline operator fun minus(i: Int): LineIndex = LineIndex(value - i)
-    inline operator fun minus(i: LineIndex): LineIndex = LineIndex(value - i.value)
-    inline operator fun compareTo(other: LineIndex): Int = value - other.value
-}
-
-/**
- * Represents an item index in the lazy grid.
- */
-@Suppress("NOTHING_TO_INLINE")
-@kotlin.jvm.JvmInline
-internal value class ItemIndex(val value: Int) {
-    inline operator fun inc(): ItemIndex = ItemIndex(value + 1)
-    inline operator fun dec(): ItemIndex = ItemIndex(value - 1)
-    inline operator fun plus(i: Int): ItemIndex = ItemIndex(value + i)
-    inline operator fun minus(i: Int): ItemIndex = ItemIndex(value - i)
-    inline operator fun minus(i: ItemIndex): ItemIndex = ItemIndex(value - i.value)
-    inline operator fun compareTo(other: ItemIndex): Int = value - other.value
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
index c752ea4..4f84478 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
@@ -286,26 +286,24 @@
                 spans = spans,
                 slots = resolvedSlots,
                 isVertical = isVertical,
-                slotsPerLine = slotsPerLine,
-                layoutDirection = layoutDirection,
                 mainAxisSpacing = mainAxisSpacing,
             )
         }
         state.prefetchInfoRetriever = { line ->
-            val lineConfiguration = spanLayoutProvider.getLineConfiguration(line.value)
-            var index = ItemIndex(lineConfiguration.firstItemIndex)
+            val lineConfiguration = spanLayoutProvider.getLineConfiguration(line)
+            var index = lineConfiguration.firstItemIndex
             var slot = 0
             val result = ArrayList<Pair<Int, Constraints>>(lineConfiguration.spans.size)
             lineConfiguration.spans.fastForEach {
                 val span = it.currentLineSpan
-                result.add(index.value to measuredLineProvider.childConstraints(slot, span))
+                result.add(index to measuredLineProvider.childConstraints(slot, span))
                 ++index
                 slot += span
             }
             result
         }
 
-        val firstVisibleLineIndex: LineIndex
+        val firstVisibleLineIndex: Int
         val firstVisibleLineScrollOffset: Int
 
         Snapshot.withoutReadObservation {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
index 2f36dec..fbd96ee 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
@@ -34,7 +34,7 @@
     private val keyToItemInfoMap = mutableMapOf<Any, ItemInfo>()
 
     // snapshot of the key to index map used for the last measuring.
-    private var keyToIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap.Empty
+    private var keyIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap.Empty
 
     // keeps the index of the first visible item index.
     private var firstVisibleIndex = 0
@@ -68,8 +68,8 @@
 
         val previousFirstVisibleIndex = firstVisibleIndex
         firstVisibleIndex = positionedItems.firstOrNull()?.index ?: 0
-        val previousKeyToIndexMap = keyToIndexMap
-        keyToIndexMap = itemProvider.keyToIndexMap
+        val previousKeyToIndexMap = keyIndexMap
+        keyIndexMap = itemProvider.keyIndexMap
 
         val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
 
@@ -92,7 +92,7 @@
                 if (itemInfo == null) {
                     keyToItemInfoMap[item.key] =
                         ItemInfo(item.getCrossAxisSize(), item.getCrossAxisOffset())
-                    val previousIndex = previousKeyToIndexMap[item.key]
+                    val previousIndex = previousKeyToIndexMap.getIndex(item.key)
                     if (previousIndex != -1 && item.index != previousIndex) {
                         if (previousIndex < previousFirstVisibleIndex) {
                             // the larger index will be in the start of the list
@@ -125,7 +125,7 @@
         var accumulatedOffset = 0
         var previousLine = -1
         var previousLineMainAxisSize = 0
-        movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
+        movingInFromStartBound.sortByDescending { previousKeyToIndexMap.getIndex(it.key) }
         movingInFromStartBound.fastForEach { item ->
             val line = if (isVertical) item.row else item.column
             if (line != -1 && line == previousLine) {
@@ -142,7 +142,7 @@
         accumulatedOffset = 0
         previousLine = -1
         previousLineMainAxisSize = 0
-        movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
+        movingInFromEndBound.sortBy { previousKeyToIndexMap.getIndex(it.key) }
         movingInFromEndBound.fastForEach { item ->
             val line = if (isVertical) item.row else item.column
             if (line != -1 && line == previousLine) {
@@ -161,13 +161,13 @@
             // found an item which was in our map previously but is not a part of the
             // positionedItems now
             val itemInfo = keyToItemInfoMap.getValue(key)
-            val newIndex = keyToIndexMap[key]
+            val newIndex = keyIndexMap.getIndex(key)
 
             if (newIndex == -1) {
                 keyToItemInfoMap.remove(key)
             } else {
                 val item = itemProvider.getAndMeasure(
-                    ItemIndex(newIndex),
+                    newIndex,
                     constraints = if (isVertical) {
                         Constraints.fixedWidth(itemInfo.crossAxisSize)
                     } else {
@@ -182,7 +182,7 @@
                         return@repeat
                     }
                 }
-                if ((!inProgress && newIndex == previousKeyToIndexMap[key])) {
+                if ((!inProgress && newIndex == previousKeyToIndexMap.getIndex(key))) {
                     keyToItemInfoMap.remove(key)
                 } else {
                     if (newIndex < firstVisibleIndex) {
@@ -197,9 +197,9 @@
         accumulatedOffset = 0
         previousLine = -1
         previousLineMainAxisSize = 0
-        movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
+        movingAwayToStartBound.sortByDescending { keyIndexMap.getIndex(it.key) }
         movingAwayToStartBound.fastForEach { item ->
-            val line = spanLayoutProvider.getLineIndexOfItem(item.index.value).value
+            val line = spanLayoutProvider.getLineIndexOfItem(item.index)
             if (line != -1 && line == previousLine) {
                 previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
             } else {
@@ -225,9 +225,9 @@
         accumulatedOffset = 0
         previousLine = -1
         previousLineMainAxisSize = 0
-        movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
+        movingAwayToEndBound.sortBy { keyIndexMap.getIndex(it.key) }
         movingAwayToEndBound.fastForEach { item ->
-            val line = spanLayoutProvider.getLineIndexOfItem(item.index.value).value
+            val line = spanLayoutProvider.getLineIndexOfItem(item.index)
             if (line != -1 && line == previousLine) {
                 previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
             } else {
@@ -264,7 +264,7 @@
      */
     fun reset() {
         keyToItemInfoMap.clear()
-        keyToIndexMap = LazyLayoutKeyIndexMap.Empty
+        keyIndexMap = LazyLayoutKeyIndexMap.Empty
         firstVisibleIndex = -1
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemProvider.kt
index 2cd5f0a7..95c2af3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemProvider.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
-import androidx.compose.foundation.lazy.layout.PinnableItem
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnableItem
 import androidx.compose.foundation.lazy.layout.NearestRangeKeyIndexMapState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
@@ -30,7 +30,7 @@
 
 @ExperimentalFoundationApi
 internal interface LazyGridItemProvider : LazyLayoutItemProvider {
-    val keyToIndexMap: LazyLayoutKeyIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap
     val spanLayoutProvider: LazyGridSpanLayoutProvider
 }
 
@@ -58,7 +58,7 @@
         LazyGridIntervalContent(latestContent())
     }
 
-    override val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
+    override val keyIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
         firstVisibleItemIndex = { state.firstVisibleItemIndex },
         slidingWindowSize = { NearestItemsSlidingWindowSize },
         extraItemCount = { NearestItemsExtraItemCount },
@@ -67,15 +67,15 @@
 
     override val itemCount: Int get() = gridContent.itemCount
 
-    override fun getKey(index: Int): Any = gridContent.getKey(index)
+    override fun getKey(index: Int): Any = keyIndexMap.getKey(index) ?: gridContent.getKey(index)
 
     override fun getContentType(index: Int): Any? = gridContent.getContentType(index)
 
     @Composable
-    override fun Item(index: Int) {
-        gridContent.PinnableItem(index, state.pinnedItems) { localIndex ->
-            with(LazyGridItemScopeImpl) {
-                item(localIndex)
+    override fun Item(index: Int, key: Any) {
+        LazyLayoutPinnableItem(key, index, state.pinnedItems) {
+            gridContent.withInterval(index) { localIndex, content ->
+                content.item(LazyGridItemScopeImpl, localIndex)
             }
         }
     }
@@ -83,7 +83,7 @@
     override val spanLayoutProvider: LazyGridSpanLayoutProvider
         get() = gridContent.spanLayoutProvider
 
-    override fun getIndex(key: Any): Int = keyToIndexMap[key]
+    override fun getIndex(key: Any): Int = keyIndexMap.getIndex(key)
 }
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
index a2a11fe..d5fa3ec 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
@@ -47,7 +47,7 @@
     beforeContentPadding: Int,
     afterContentPadding: Int,
     spaceBetweenLines: Int,
-    firstVisibleLineIndex: LineIndex,
+    firstVisibleLineIndex: Int,
     firstVisibleLineScrollOffset: Int,
     scrollToBeConsumed: Float,
     constraints: Constraints,
@@ -92,7 +92,7 @@
         currentFirstLineScrollOffset -= scrollDelta
 
         // if the current scroll offset is less than minimally possible
-        if (currentFirstLineIndex == LineIndex(0) && currentFirstLineScrollOffset < 0) {
+        if (currentFirstLineIndex == 0 && currentFirstLineScrollOffset < 0) {
             scrollDelta += currentFirstLineScrollOffset
             currentFirstLineScrollOffset = 0
         }
@@ -112,8 +112,8 @@
         // we had scrolled backward or we compose items in the start padding area, which means
         // items before current firstLineScrollOffset should be visible. compose them and update
         // firstLineScrollOffset
-        while (currentFirstLineScrollOffset < 0 && currentFirstLineIndex > LineIndex(0)) {
-            val previous = LineIndex(currentFirstLineIndex.value - 1)
+        while (currentFirstLineScrollOffset < 0 && currentFirstLineIndex > 0) {
+            val previous = currentFirstLineIndex - 1
             val measuredLine = measuredLineProvider.getAndMeasure(previous)
             visibleLines.add(0, measuredLine)
             currentFirstLineScrollOffset += measuredLine.mainAxisSizeWithSpacings
@@ -143,7 +143,7 @@
         // then composing visible lines forward until we fill the whole viewport.
         // we want to have at least one line in visibleItems even if in fact all the items are
         // offscreen, this can happen if the content padding is larger than the available size.
-        while (index.value < itemsCount &&
+        while (index < itemsCount &&
             (currentMainAxisOffset < maxMainAxis ||
                 currentMainAxisOffset <= 0 || // filling beforeContentPadding area
                 visibleLines.isEmpty())
@@ -155,7 +155,7 @@
 
             currentMainAxisOffset += measuredLine.mainAxisSizeWithSpacings
             if (currentMainAxisOffset <= minOffset &&
-                measuredLine.items.last().index.value != itemsCount - 1) {
+                measuredLine.items.last().index != itemsCount - 1) {
                 // this line is offscreen and will not be placed. advance firstVisibleLineIndex
                 currentFirstLineIndex = index + 1
                 currentFirstLineScrollOffset -= measuredLine.mainAxisSizeWithSpacings
@@ -172,9 +172,9 @@
             currentFirstLineScrollOffset -= toScrollBack
             currentMainAxisOffset += toScrollBack
             while (currentFirstLineScrollOffset < beforeContentPadding &&
-                currentFirstLineIndex > LineIndex(0)
+                currentFirstLineIndex > 0
             ) {
-                val previousIndex = LineIndex(currentFirstLineIndex.value - 1)
+                val previousIndex = currentFirstLineIndex - 1
                 val measuredLine = measuredLineProvider.getAndMeasure(previousIndex)
                 visibleLines.add(0, measuredLine)
                 currentFirstLineScrollOffset += measuredLine.mainAxisSizeWithSpacings
@@ -205,8 +205,8 @@
         val visibleLinesScrollOffset = -currentFirstLineScrollOffset
         var firstLine = visibleLines.first()
 
-        val firstItemIndex = firstLine.items.firstOrNull()?.index?.value ?: 0
-        val lastItemIndex = visibleLines.lastOrNull()?.items?.lastOrNull()?.index?.value ?: 0
+        val firstItemIndex = firstLine.items.firstOrNull()?.index ?: 0
+        val lastItemIndex = visibleLines.lastOrNull()?.items?.lastOrNull()?.index ?: 0
         val extraItemsBefore = calculateExtraItems(
             pinnedItems,
             measuredItemProvider,
@@ -304,17 +304,16 @@
 private inline fun calculateExtraItems(
     pinnedItems: List<Int>,
     measuredItemProvider: LazyGridMeasuredItemProvider,
-    itemConstraints: (ItemIndex) -> Constraints,
+    itemConstraints: (Int) -> Constraints,
     filter: (Int) -> Boolean
 ): List<LazyGridMeasuredItem> {
     var items: MutableList<LazyGridMeasuredItem>? = null
 
     pinnedItems.fastForEach { index ->
         if (filter(index)) {
-            val itemIndex = ItemIndex(index)
-            val constraints = itemConstraints(itemIndex)
+            val constraints = itemConstraints(index)
             val measuredItem = measuredItemProvider.getAndMeasure(
-                itemIndex,
+                index,
                 constraints = constraints
             )
             if (items == null) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt
index 9db0fd0..74a6896 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt
@@ -28,7 +28,7 @@
  * if the user emit multiple layout nodes in the item callback.
  */
 internal class LazyGridMeasuredItem(
-    val index: ItemIndex,
+    val index: Int,
     val key: Any,
     private val isVertical: Boolean,
     /**
@@ -101,7 +101,7 @@
             } else {
                 IntOffset(mainAxisOffset, crossAxisOffset)
             },
-            index = index.value,
+            index = index,
             key = key,
             row = row,
             column = column,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt
index ae9a1d8e..c984631 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt
@@ -37,13 +37,13 @@
      * with the provided [constraints] and wrapped into [LazyGridMeasuredItem].
      */
     fun getAndMeasure(
-        index: ItemIndex,
+        index: Int,
         mainAxisSpacing: Int = defaultMainAxisSpacing,
         constraints: Constraints
     ): LazyGridMeasuredItem {
-        val key = itemProvider.getKey(index.value)
-        val contentType = itemProvider.getContentType(index.value)
-        val placeables = measureScope.measure(index.value, constraints)
+        val key = keyIndexMap.getKey(index) ?: itemProvider.getKey(index)
+        val contentType = itemProvider.getContentType(index)
+        val placeables = measureScope.measure(index, constraints)
         val crossAxisSize = if (constraints.hasFixedWidth) {
             constraints.minWidth
         } else {
@@ -64,13 +64,13 @@
      * Contains the mapping between the key and the index. It could contain not all the items of
      * the list as an optimization.
      **/
-    val keyToIndexMap: LazyLayoutKeyIndexMap get() = itemProvider.keyToIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap = itemProvider.keyIndexMap
 }
 
 // This interface allows to avoid autoboxing on index param
 internal fun interface MeasuredItemFactory {
     fun createItem(
-        index: ItemIndex,
+        index: Int,
         key: Any,
         contentType: Any?,
         crossAxisSize: Int,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt
index b70f8bf..0b3f5e0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.lazy.grid
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.ui.unit.LayoutDirection
 
 /**
  * Represents one measured line of the lazy list. Each item on the line can in fact consist of
@@ -25,13 +24,11 @@
  */
 @OptIn(ExperimentalFoundationApi::class)
 internal class LazyGridMeasuredLine constructor(
-    val index: LineIndex,
+    val index: Int,
     val items: Array<LazyGridMeasuredItem>,
     private val slots: LazyGridSlots,
     private val spans: List<GridItemSpan>,
     private val isVertical: Boolean,
-    private val slotsPerLine: Int,
-    private val layoutDirection: LayoutDirection,
     /**
      * Spacing to be added after [mainAxisSize], in the main axis direction.
      */
@@ -80,8 +77,8 @@
                 crossAxisOffset = slots.positions[startSlot],
                 layoutWidth = layoutWidth,
                 layoutHeight = layoutHeight,
-                row = if (isVertical) index.value else startSlot,
-                column = if (isVertical) startSlot else index.value
+                row = if (isVertical) index else startSlot,
+                column = if (isVertical) startSlot else index
             ).also {
                 usedSpan += span
             }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt
index f0d8250..fe5ff54 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt
@@ -48,9 +48,9 @@
         }
     }
 
-    fun itemConstraints(itemIndex: ItemIndex): Constraints {
+    fun itemConstraints(itemIndex: Int): Constraints {
         val span = spanLayoutProvider.spanOf(
-            itemIndex.value,
+            itemIndex,
             spanLayoutProvider.slotsPerLine
         )
         return childConstraints(0, span)
@@ -60,8 +60,8 @@
      * Used to subcompose items on lines of lazy grids. Composed placeables will be measured
      * with the correct constraints and wrapped into [LazyGridMeasuredLine].
      */
-    fun getAndMeasure(lineIndex: LineIndex): LazyGridMeasuredLine {
-        val lineConfiguration = spanLayoutProvider.getLineConfiguration(lineIndex.value)
+    fun getAndMeasure(lineIndex: Int): LazyGridMeasuredLine {
+        val lineConfiguration = spanLayoutProvider.getLineConfiguration(lineIndex)
         val lineItemsCount = lineConfiguration.spans.size
 
         // we add space between lines as an extra spacing for all lines apart from the last one
@@ -78,7 +78,7 @@
             val span = lineConfiguration.spans[it].currentLineSpan
             val constraints = childConstraints(startSlot, span)
             measuredItemProvider.getAndMeasure(
-                ItemIndex(lineConfiguration.firstItemIndex + it),
+                lineConfiguration.firstItemIndex + it,
                 mainAxisSpacing,
                 constraints
             ).also { startSlot += span }
@@ -95,14 +95,14 @@
      * Contains the mapping between the key and the index. It could contain not all the items of
      * the list as an optimization.
      **/
-    val keyToIndexMap: LazyLayoutKeyIndexMap get() = measuredItemProvider.keyToIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap get() = measuredItemProvider.keyIndexMap
 }
 
 // This interface allows to avoid autoboxing on index param
 @OptIn(ExperimentalFoundationApi::class)
 internal fun interface MeasuredLineFactory {
     fun createLine(
-        index: LineIndex,
+        index: Int,
         items: Array<LazyGridMeasuredItem>,
         spans: List<GridItemSpan>,
         mainAxisSpacing: Int
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
index 32575e7..7b19619 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.findIndexByKey
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
 
@@ -32,10 +32,10 @@
     initialIndex: Int = 0,
     initialScrollOffset: Int = 0
 ) {
-    var index by mutableStateOf(ItemIndex(initialIndex))
+    var index by mutableIntStateOf(initialIndex)
         private set
 
-    var scrollOffset by mutableStateOf(initialScrollOffset)
+    var scrollOffset by mutableIntStateOf(initialScrollOffset)
         private set
 
     private var hadFirstNotEmptyLayout = false
@@ -58,9 +58,7 @@
 
             Snapshot.withoutReadObservation {
                 update(
-                    ItemIndex(
-                        measureResult.firstVisibleLine?.items?.firstOrNull()?.index?.value ?: 0
-                    ),
+                    measureResult.firstVisibleLine?.items?.firstOrNull()?.index ?: 0,
                     scrollOffset
                 )
             }
@@ -78,7 +76,7 @@
      * c) there will be not enough items to fill the viewport after the requested index, so we
      * would have to compose few elements before the asked index, changing the first visible item.
      */
-    fun requestPosition(index: ItemIndex, scrollOffset: Int) {
+    fun requestPosition(index: Int, scrollOffset: Int) {
         update(index, scrollOffset)
         // clear the stored key as we have a direct request to scroll to [index] position and the
         // next [checkIfFirstVisibleItemWasMoved] shouldn't override this.
@@ -94,14 +92,14 @@
     fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyGridItemProvider) {
         Snapshot.withoutReadObservation {
             update(
-                ItemIndex(itemProvider.findIndexByKey(lastKnownFirstItemKey, index.value)),
+                itemProvider.findIndexByKey(lastKnownFirstItemKey, index),
                 scrollOffset
             )
         }
     }
 
-    private fun update(index: ItemIndex, scrollOffset: Int) {
-        require(index.value >= 0f) { "Index should be non-negative (${index.value})" }
+    private fun update(index: Int, scrollOffset: Int) {
+        require(index >= 0f) { "Index should be non-negative ($index)" }
         if (index != this.index) {
             this.index = index
         }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpanLayoutProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpanLayoutProvider.kt
index c5c802a..ac6ac2a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpanLayoutProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpanLayoutProvider.kt
@@ -167,13 +167,13 @@
     /**
      * Calculate the line of index [itemIndex].
      */
-    fun getLineIndexOfItem(itemIndex: Int): LineIndex {
+    fun getLineIndexOfItem(itemIndex: Int): Int {
         if (totalSize <= 0) {
-            return LineIndex(0)
+            return 0
         }
         require(itemIndex < totalSize)
         if (!gridContent.hasCustomSpans) {
-            return LineIndex(itemIndex / slotsPerLine)
+            return itemIndex / slotsPerLine
         }
 
         val lowerBoundBucket = buckets.binarySearch { it.firstItemIndex - itemIndex }.let {
@@ -207,7 +207,7 @@
             ++currentLine
         }
 
-        return LineIndex(currentLine)
+        return currentLine
     }
 
     fun spanOf(itemIndex: Int, maxSpan: Int): Int =
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
index 2127ee7..193dfe2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
@@ -23,7 +23,7 @@
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.lazy.AwaitFirstLayoutModifier
+import androidx.compose.foundation.lazy.layout.AwaitFirstLayoutModifier
 import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.listSaver
@@ -104,7 +105,7 @@
      * derived state in order to only have recompositions when the derived value changes:
      * @sample androidx.compose.foundation.samples.UsingGridScrollPositionInCompositionSample
      */
-    val firstVisibleItemIndex: Int get() = scrollPosition.index.value
+    val firstVisibleItemIndex: Int get() = scrollPosition.index
 
     /**
      * The scroll offset of the first visible item. Scrolling forward is positive - i.e., the
@@ -149,7 +150,7 @@
     /**
      * Needed for [animateScrollToItem]. Updated on every measure.
      */
-    internal var slotsPerLine: Int by mutableStateOf(0)
+    internal var slotsPerLine: Int by mutableIntStateOf(0)
 
     /**
      * Needed for [animateScrollToItem]. Updated on every measure.
@@ -221,7 +222,7 @@
     /**
      * Finds items on a line and their measurement constraints. Used for prefetching.
      */
-    internal var prefetchInfoRetriever: (line: LineIndex) -> List<Pair<Int, Constraints>> by
+    internal var prefetchInfoRetriever: (line: Int) -> List<Pair<Int, Constraints>> by
         mutableStateOf({ emptyList() })
 
     internal val placementAnimator = LazyGridItemPlacementAnimator()
@@ -255,7 +256,7 @@
     }
 
     internal fun snapToItemIndexInternal(index: Int, scrollOffset: Int) {
-        scrollPosition.requestPosition(ItemIndex(index), scrollOffset)
+        scrollPosition.requestPosition(index, scrollOffset)
         // placement animation is not needed because we snap into a new position.
         placementAnimator.reset()
         remeasurement?.forceRemeasure()
@@ -359,7 +360,7 @@
                 this.wasScrollingForward = scrollingForward
                 this.lineToPrefetch = lineToPrefetch
                 currentLinePrefetchHandles.clear()
-                prefetchInfoRetriever(LineIndex(lineToPrefetch)).fastForEach {
+                prefetchInfoRetriever(lineToPrefetch).fastForEach {
                     currentLinePrefetchHandles.add(
                         prefetchState.schedulePrefetch(it.first, it.second)
                     )
@@ -414,7 +415,7 @@
         layoutInfoState.value = result
 
         canScrollForward = result.canScrollForward
-        canScrollBackward = (result.firstVisibleLine?.index?.value ?: 0) != 0 ||
+        canScrollBackward = (result.firstVisibleLine?.index ?: 0) != 0 ||
             result.firstVisibleLineScrollOffset != 0
 
         numMeasurePasses++
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/AwaitFirstLayoutModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/AwaitFirstLayoutModifier.kt
new file mode 100644
index 0000000..c7a7759
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/AwaitFirstLayoutModifier.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.layout
+
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.OnGloballyPositionedModifier
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+/**
+ * Internal modifier which allows to delay some interactions (e.g. scroll) until layout is ready.
+ */
+internal class AwaitFirstLayoutModifier : OnGloballyPositionedModifier {
+    private var wasPositioned = false
+    private var continuation: Continuation<Unit>? = null
+
+    suspend fun waitForFirstLayout() {
+        if (!wasPositioned) {
+            val oldContinuation = continuation
+            suspendCoroutine { continuation = it }
+            oldContinuation?.resume(Unit)
+        }
+    }
+
+    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+        if (!wasPositioned) {
+            wasPositioned = true
+            continuation?.resume(Unit)
+            continuation = null
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent.kt
index aeb9028..90665f1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.lazy.layout
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.runtime.Composable
 
 /**
  * Common parts backing the interval-based content of lazy layout defined through `item` DSL.
@@ -26,24 +25,37 @@
 abstract class LazyLayoutIntervalContent<Interval : LazyLayoutIntervalContent.Interval> {
     abstract val intervals: IntervalList<Interval>
 
+    /**
+     * The total amount of items in all the intervals.
+     */
     val itemCount: Int get() = intervals.size
 
+    /**
+     * Returns item key based on a global index.
+     */
     fun getKey(index: Int): Any =
-        withLocalIntervalIndex(index) { localIndex, content ->
+        withInterval(index) { localIndex, content ->
             content.key?.invoke(localIndex) ?: getDefaultLazyLayoutKey(index)
         }
 
+    /**
+     * Returns content type based on a global index.
+     */
     fun getContentType(index: Int): Any? =
-        withLocalIntervalIndex(index) { localIndex, content ->
+        withInterval(index) { localIndex, content ->
             content.type.invoke(localIndex)
         }
 
-    private inline fun <T> withLocalIntervalIndex(
-        index: Int,
-        block: (localIndex: Int, content: Interval) -> T
+    /**
+     * Runs a [block] on the content of the interval associated with the provided [globalIndex]
+     * with providing a local index in the given interval.
+     */
+    inline fun <T> withInterval(
+        globalIndex: Int,
+        block: (localIntervalIndex: Int, content: Interval) -> T
     ): T {
-        val interval = intervals[index]
-        val localIntervalIndex = index - interval.startIndex
+        val interval = intervals[globalIndex]
+        val localIntervalIndex = globalIndex - interval.startIndex
         return block(localIntervalIndex, interval.value)
     }
 
@@ -63,25 +75,3 @@
         val type: ((index: Int) -> Any?) get() = { null }
     }
 }
-
-/**
- * Defines a composable content of item in a lazy layout to support focus pinning.
- * See [LazyLayoutPinnableItem] for more details.
- */
-@ExperimentalFoundationApi
-@Composable
-fun <T : LazyLayoutIntervalContent.Interval> LazyLayoutIntervalContent<T>.PinnableItem(
-    index: Int,
-    pinnedItemList: LazyLayoutPinnedItemList,
-    content: @Composable T.(index: Int) -> Unit
-) {
-    val interval = intervals[index]
-    val localIndex = index - interval.startIndex
-    LazyLayoutPinnableItem(
-        interval.value.key?.invoke(localIndex),
-        index,
-        pinnedItemList
-    ) {
-        interval.value.content(localIndex)
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
index 9b61189..0b3039b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
@@ -22,7 +22,7 @@
 import androidx.compose.runtime.ReusableContentHost
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.saveable.SaveableStateHolder
 import androidx.compose.runtime.setValue
 
@@ -84,7 +84,7 @@
         val key: Any,
         val type: Any?
     ) {
-        var lastKnownIndex by mutableStateOf(initialIndex)
+        var lastKnownIndex by mutableIntStateOf(initialIndex)
             private set
 
         private var _content: (@Composable () -> Unit)? = null
@@ -93,15 +93,20 @@
 
         private fun createContentLambda() = @Composable {
             val itemProvider = itemProvider()
-            val index = itemProvider.findIndexByKey(key, lastKnownIndex).also {
-                lastKnownIndex = it
+
+            var index = lastKnownIndex
+            if (index >= itemProvider.itemCount || itemProvider.getKey(index) != key) {
+                index = itemProvider.getIndex(key)
+                if (index != -1) lastKnownIndex = index
             }
-            val indexIsUpToDate =
-                index < itemProvider.itemCount && itemProvider.getKey(index) == key
-            ReusableContentHost(active = indexIsUpToDate) {
-                StableSaveProvider(StableValue(saveableStateHolder), StableValue(key)) {
-                    itemProvider.Item(index)
-                }
+
+            ReusableContentHost(active = index != -1) {
+                SkippableItem(
+                    itemProvider,
+                    StableValue(saveableStateHolder),
+                    index,
+                    StableValue(key)
+                )
             }
             DisposableEffect(key) {
                 onDispose {
@@ -118,14 +123,18 @@
 private value class StableValue<T>(val value: T)
 
 /**
- * Hack around skippable functions to force restart for unstable saveable state holder that uses
- * [Any] as key.
+ * Hack around skippable functions to force skip SaveableStateProvider and Item block when
+ * nothing changed. It allows us to skip heavy-weight composition local providers.
  */
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
-private fun StableSaveProvider(
+private fun SkippableItem(
+    itemProvider: LazyLayoutItemProvider,
     saveableStateHolder: StableValue<SaveableStateHolder>,
-    key: StableValue<Any>,
-    content: @Composable () -> Unit
+    index: Int,
+    key: StableValue<Any>
 ) {
-    saveableStateHolder.value.SaveableStateProvider(key.value, content)
+    saveableStateHolder.value.SaveableStateProvider(key.value) {
+        itemProvider.Item(index, key.value)
+    }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemProvider.kt
index 98a6b0a..9498578 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemProvider.kt
@@ -34,10 +34,10 @@
     val itemCount: Int
 
     /**
-     * The item for the given [index].
+     * The item for the given [index] and [key].
      */
     @Composable
-    fun Item(index: Int)
+    fun Item(index: Int, key: Any)
 
     /**
      * Returns the content type for the item on this index. It is used to improve the item
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutKeyIndexMap.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutKeyIndexMap.kt
index c46335a..a6485ee 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutKeyIndexMap.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutKeyIndexMap.kt
@@ -33,14 +33,20 @@
     /**
      * @return current index for given [key] or `-1` if not found.
      */
-    operator fun get(key: Any): Int
+    fun getIndex(key: Any): Int
+
+    /**
+     * @return key for a given [index] if it is known, or null otherwise.
+     */
+    fun getKey(index: Int): Any?
 
     /**
      * Empty map implementation, always returning `-1` for any key.
      */
     companion object Empty : LazyLayoutKeyIndexMap {
         @Suppress("AutoBoxing")
-        override fun get(key: Any): Int = -1
+        override fun getIndex(key: Any): Int = -1
+        override fun getKey(index: Int) = null
     }
 }
 
@@ -90,44 +96,55 @@
     nearestRange: IntRange,
     content: LazyLayoutIntervalContent<*>
 ) : LazyLayoutKeyIndexMap {
-    private val map = generateKeyToIndexMap(nearestRange, content.intervals)
+    private val map: Map<Any, Int>
+    private val keys: Array<Any?>
+    private val keysStartIndex: Int
 
-    override fun get(key: Any): Int = map.getOrElse(key) { -1 }
-
-    companion object {
-        /**
-         * Traverses the interval [list] in order to create a mapping from the key to the index for all
-         * the indexes in the passed [range].
-         * The returned map will not contain the values for intervals with no key mapping provided.
-         */
-        private fun generateKeyToIndexMap(
-            range: IntRange,
-            list: IntervalList<LazyLayoutIntervalContent.Interval>
-        ): Map<Any, Int> {
-            val first = range.first
-            check(first >= 0)
-            val last = minOf(range.last, list.size - 1)
-            return if (last < first) {
-                emptyMap()
-            } else {
-                hashMapOf<Any, Int>().also { map ->
-                    list.forEach(
-                        fromIndex = first,
-                        toIndex = last,
-                    ) {
-                        if (it.value.key != null) {
-                            val keyFactory = requireNotNull(it.value.key)
-                            val start = maxOf(first, it.startIndex)
-                            val end = minOf(last, it.startIndex + it.size - 1)
-                            for (i in start..end) {
-                                map[keyFactory(i - it.startIndex)] = i
-                            }
+    init {
+        // Traverses the interval [list] in order to create a mapping from the key to the index for all
+        // the indexes in the passed [range].
+        // The returned map will not contain the values for intervals with no key mapping provided.
+        val list = content.intervals
+        val first = nearestRange.first
+        check(first >= 0)
+        val last = minOf(nearestRange.last, list.size - 1)
+        if (last < first) {
+            map = emptyMap()
+            keys = emptyArray()
+            keysStartIndex = 0
+        } else {
+            var tmpKeys = emptyArray<Any?>()
+            var tmpKeysStartIndex = 0
+            map = hashMapOf<Any, Int>().also { map ->
+                list.forEach(
+                    fromIndex = first,
+                    toIndex = last,
+                ) {
+                    if (it.value.key != null) {
+                        val keyFactory = requireNotNull(it.value.key)
+                        val start = maxOf(first, it.startIndex)
+                        if (tmpKeys.isEmpty()) {
+                            tmpKeysStartIndex = start
+                            tmpKeys = Array(last - start + 1) { null }
+                        }
+                        val end = minOf(last, it.startIndex + it.size - 1)
+                        for (i in start..end) {
+                            val key = keyFactory(i - it.startIndex)
+                            map[key] = i
+                            tmpKeys[i - tmpKeysStartIndex] = key
                         }
                     }
                 }
             }
+            keys = tmpKeys
+            keysStartIndex = tmpKeysStartIndex
         }
     }
+
+    override fun getIndex(key: Any): Int = map.getOrElse(key) { -1 }
+
+    override fun getKey(index: Int) =
+        keys.getOrElse(index - keysStartIndex) { null }
 }
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPinnableItem.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPinnableItem.kt
index 768c234..680f8b5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPinnableItem.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPinnableItem.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -101,13 +102,13 @@
     /**
      * Current index associated with this item.
      */
-    override var index by mutableStateOf(-1)
+    override var index by mutableIntStateOf(-1)
 
     /**
      * It is a valid use case when users of this class call [pin] multiple times individually,
      * so we want to do the unpinning only when all of the users called [release].
      */
-    private var pinsCount by mutableStateOf(0)
+    private var pinsCount by mutableIntStateOf(0)
 
     /**
      * Handle associated with the current [parentPinnableContainer].
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
index 7faf82e..79423ba 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
@@ -81,6 +81,7 @@
     LazyLayout(
         modifier = modifier
             .then(state.remeasurementModifier)
+            .then(state.awaitLayoutModifier)
             .lazyLayoutSemantics(
                 itemProvider = itemProvider,
                 state = semanticState,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
index 11895ed..f9dec0d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
@@ -34,7 +34,7 @@
     private val keyToItemInfoMap = mutableMapOf<Any, ItemInfo>()
 
     // snapshot of the key to index map used for the last measuring.
-    private var keyToIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap
+    private var keyIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap
 
     // keeps the index of the first visible item index.
     private var firstVisibleIndex = 0
@@ -68,8 +68,8 @@
 
         val previousFirstVisibleIndex = firstVisibleIndex
         firstVisibleIndex = positionedItems.firstOrNull()?.index ?: 0
-        val previousKeyToIndexMap = keyToIndexMap
-        keyToIndexMap = itemProvider.keyToIndexMap
+        val previousKeyToIndexMap = keyIndexMap
+        keyIndexMap = itemProvider.keyIndexMap
 
         val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
 
@@ -92,7 +92,7 @@
                 if (itemInfo == null) {
                     keyToItemInfoMap[item.key] =
                         ItemInfo(item.lane, item.span, item.crossAxisOffset)
-                    val previousIndex = previousKeyToIndexMap[item.key]
+                    val previousIndex = previousKeyToIndexMap.getIndex(item.key)
                     if (previousIndex != -1 && item.index != previousIndex) {
                         if (previousIndex < previousFirstVisibleIndex) {
                             // the larger index will be in the start of the list
@@ -125,7 +125,7 @@
 
         val accumulatedOffsetPerLane = IntArray(laneCount) { 0 }
         if (movingInFromStartBound.isNotEmpty()) {
-            movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
+            movingInFromStartBound.sortByDescending { previousKeyToIndexMap.getIndex(it.key) }
             movingInFromStartBound.fastForEach { item ->
                 accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
                 val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
@@ -135,7 +135,7 @@
             accumulatedOffsetPerLane.fill(0)
         }
         if (movingInFromEndBound.isNotEmpty()) {
-            movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
+            movingInFromEndBound.sortBy { previousKeyToIndexMap.getIndex(it.key) }
             movingInFromEndBound.fastForEach { item ->
                 val mainAxisOffset = mainAxisLayoutSize + accumulatedOffsetPerLane[item.lane]
                 accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
@@ -149,7 +149,7 @@
             // found an item which was in our map previously but is not a part of the
             // positionedItems now
             val itemInfo = keyToItemInfoMap.getValue(key)
-            val newIndex = keyToIndexMap[key]
+            val newIndex = keyIndexMap.getIndex(key)
 
             if (newIndex == -1) {
                 keyToItemInfoMap.remove(key)
@@ -166,7 +166,7 @@
                         return@repeat
                     }
                 }
-                if ((!inProgress && newIndex == previousKeyToIndexMap[key])) {
+                if ((!inProgress && newIndex == previousKeyToIndexMap.getIndex(key))) {
                     keyToItemInfoMap.remove(key)
                 } else {
                     if (newIndex < firstVisibleIndex) {
@@ -179,7 +179,7 @@
         }
 
         if (movingAwayToStartBound.isNotEmpty()) {
-            movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
+            movingAwayToStartBound.sortByDescending { keyIndexMap.getIndex(it.key) }
             movingAwayToStartBound.fastForEach { item ->
                 accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
                 val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
@@ -193,7 +193,7 @@
             accumulatedOffsetPerLane.fill(0)
         }
         if (movingAwayToEndBound.isNotEmpty()) {
-            movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
+            movingAwayToEndBound.sortBy { keyIndexMap.getIndex(it.key) }
             movingAwayToEndBound.fastForEach { item ->
                 val mainAxisOffset = mainAxisLayoutSize + accumulatedOffsetPerLane[item.lane]
                 accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
@@ -219,7 +219,7 @@
      */
     fun reset() {
         keyToItemInfoMap.clear()
-        keyToIndexMap = LazyLayoutKeyIndexMap
+        keyIndexMap = LazyLayoutKeyIndexMap
         firstVisibleIndex = -1
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt
index b54bd72..b76423c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
-import androidx.compose.foundation.lazy.layout.PinnableItem
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnableItem
 import androidx.compose.foundation.lazy.layout.NearestRangeKeyIndexMapState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
@@ -31,7 +31,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 internal interface LazyStaggeredGridItemProvider : LazyLayoutItemProvider {
     val spanProvider: LazyStaggeredGridSpanProvider
-    val keyToIndexMap: LazyLayoutKeyIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap
 }
 
 @Composable
@@ -57,7 +57,7 @@
         LazyStaggeredGridIntervalContent(latestContent())
     }
 
-    override val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
+    override val keyIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
         firstVisibleItemIndex = { state.firstVisibleItemIndex },
         slidingWindowSize = { 90 },
         extraItemCount = { 200 },
@@ -66,17 +66,18 @@
 
     override val itemCount: Int get() = staggeredGridContent.itemCount
 
-    override fun getKey(index: Int): Any = staggeredGridContent.getKey(index)
+    override fun getKey(index: Int): Any =
+        keyIndexMap.getKey(index) ?: staggeredGridContent.getKey(index)
 
-    override fun getIndex(key: Any): Int = keyToIndexMap[key]
+    override fun getIndex(key: Any): Int = keyIndexMap.getIndex(key)
 
     override fun getContentType(index: Int): Any? = staggeredGridContent.getContentType(index)
 
     @Composable
-    override fun Item(index: Int) {
-        staggeredGridContent.PinnableItem(index, state.pinnedItems) { localIndex ->
-            with(LazyStaggeredGridItemScopeImpl) {
-                item(localIndex)
+    override fun Item(index: Int, key: Any) {
+        LazyLayoutPinnableItem(key, index, state.pinnedItems) {
+            staggeredGridContent.withInterval(index) { localIndex, content ->
+                content.item(LazyStaggeredGridItemScopeImpl, localIndex)
             }
         }
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index 62c4e2c..d92ad20 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -1013,7 +1013,7 @@
     }
 
     fun getAndMeasure(index: Int, span: SpanRange): LazyStaggeredGridMeasuredItem {
-        val key = itemProvider.getKey(index)
+        val key = keyIndexMap.getKey(index) ?: itemProvider.getKey(index)
         val contentType = itemProvider.getContentType(index)
         val placeables = measureScope.measure(index, childConstraints(span.start, span.size))
         return measuredItemFactory.createItem(
@@ -1026,7 +1026,7 @@
         )
     }
 
-    val keyToIndexMap: LazyLayoutKeyIndexMap get() = itemProvider.keyToIndexMap
+    val keyIndexMap: LazyLayoutKeyIndexMap = itemProvider.keyIndexMap
 }
 
 // This interface allows to avoid autoboxing on index param
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index a1b62d6..35b1261 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.lazy.layout.AwaitFirstLayoutModifier
 import androidx.compose.foundation.lazy.layout.LazyAnimateScrollScope
 import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
@@ -166,6 +167,12 @@
         }
     }
 
+    /**
+     * Provides a modifier which allows to delay some interactions (e.g. scroll)
+     * until layout is ready.
+     */
+    internal val awaitLayoutModifier = AwaitFirstLayoutModifier()
+
     internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
 
     /**
@@ -228,6 +235,7 @@
         scrollPriority: MutatePriority,
         block: suspend ScrollScope.() -> Unit
     ) {
+        awaitLayoutModifier.waitForFirstLayout()
         scrollableState.scroll(scrollPriority, block)
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index 5d8d4093..d42eaed 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -32,9 +32,9 @@
 import androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnableItem
 import androidx.compose.foundation.lazy.layout.MutableIntervalList
 import androidx.compose.foundation.lazy.layout.NearestRangeKeyIndexMapState
-import androidx.compose.foundation.lazy.layout.PinnableItem
 import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
 import androidx.compose.foundation.overscroll
 import androidx.compose.runtime.Composable
@@ -199,15 +199,17 @@
         get() = pagerContent.itemCount
 
     @Composable
-    override fun Item(index: Int) {
-        pagerContent.PinnableItem(index, state.pinnedPages) { localIndex ->
-            item(pagerScopeImpl, localIndex)
+    override fun Item(index: Int, key: Any) {
+        LazyLayoutPinnableItem(key, index, state.pinnedPages) {
+            pagerContent.withInterval(index) { localIndex, content ->
+                content.item(pagerScopeImpl, localIndex)
+            }
         }
     }
 
-    override fun getKey(index: Int): Any = pagerContent.getKey(index)
+    override fun getKey(index: Int): Any = keyToIndexMap.getKey(index) ?: pagerContent.getKey(index)
 
-    override fun getIndex(key: Any): Int = keyToIndexMap[key]
+    override fun getIndex(key: Any): Int = keyToIndexMap.getIndex(key)
 }
 
 @OptIn(ExperimentalFoundationApi::class)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
index cf2cb6b..cecb371 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
 
@@ -31,10 +31,10 @@
     initialPage: Int = 0,
     initialScrollOffset: Int = 0
 ) {
-    var firstVisiblePage by mutableStateOf(initialPage)
-    var currentPage by mutableStateOf(initialPage)
+    var firstVisiblePage by mutableIntStateOf(initialPage)
+    var currentPage by mutableIntStateOf(initialPage)
 
-    var scrollOffset by mutableStateOf(initialScrollOffset)
+    var scrollOffset by mutableIntStateOf(initialScrollOffset)
         private set
 
     private var hadFirstNotEmptyLayout = false
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index 1e9132b..715fe0a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -27,7 +27,7 @@
 import androidx.compose.foundation.gestures.animateScrollBy
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.lazy.AwaitFirstLayoutModifier
+import androidx.compose.foundation.lazy.layout.AwaitFirstLayoutModifier
 import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
 import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
@@ -35,6 +35,8 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.listSaver
@@ -181,7 +183,7 @@
      * Difference between the last up and last down events of a scroll event.
      */
     internal var upDownDifference: Offset by mutableStateOf(Offset.Zero)
-    internal var snapRemainingScrollOffset by mutableStateOf(0f)
+    internal var snapRemainingScrollOffset by mutableFloatStateOf(0f)
 
     private val scrollPosition = PagerScrollPosition(initialPage, 0)
 
@@ -283,9 +285,9 @@
      */
     val currentPage: Int get() = scrollPosition.currentPage
 
-    private var animationTargetPage by mutableStateOf(-1)
+    private var animationTargetPage by mutableIntStateOf(-1)
 
-    private var settledPageState by mutableStateOf(initialPage)
+    private var settledPageState by mutableIntStateOf(initialPage)
 
     /**
      * The page that is currently "settled". This is an animation/gesture unaware page in the sense
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
index 0122d8e..e238c52 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
@@ -25,6 +25,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.listSaver
@@ -250,13 +251,13 @@
      * Taken with the opposite sign defines the x or y position of the text field in the
      * horizontal or vertical scroller container correspondingly.
      */
-    var offset by mutableStateOf(initial)
+    var offset by mutableFloatStateOf(initial)
 
     /**
      * Maximum length by which the text field can be scrolled. Defined as a difference in
      * size between the scroller container and the text field.
      */
-    var maximum by mutableStateOf(0f)
+    var maximum by mutableFloatStateOf(0f)
         private set
 
     /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
index fccf555..76386b2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
@@ -73,7 +73,7 @@
     private var minLines: Int = DefaultMinLines,
     private var overrideColor: ColorProducer? = null
 ) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
-    private var baselineCache: Map<AlignmentLine, Int>? = null
+    private var baselineCache: MutableMap<AlignmentLine, Int>? = null
 
     private var _layoutCache: ParagraphLayoutCache? = null
     private val layoutCache: ParagraphLayoutCache
@@ -224,10 +224,13 @@
 
         if (didChangeLayout) {
             invalidateLayer()
-            baselineCache = mapOf(
-                FirstBaseline to paragraph.firstBaseline.roundToInt(),
-                LastBaseline to paragraph.lastBaseline.roundToInt()
-            )
+            var cache = baselineCache
+            if (cache == null) {
+                cache = LinkedHashMap(2)
+            }
+            cache[FirstBaseline] = paragraph.firstBaseline.roundToInt()
+            cache[LastBaseline] = paragraph.lastBaseline.roundToInt()
+            baselineCache = cache
         }
 
         // then allow children to measure _inside_ our final box, with the above placeholders
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt
index b285b1c..fff404c 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt
@@ -36,6 +36,7 @@
  * WindowDraggableArea is a component that allows you to drag the window using the mouse.
  *
  * @param modifier The modifier to be applied to the layout.
+ * @param content The content lambda.
  */
 @Composable
 fun WindowScope.WindowDraggableArea(
diff --git a/compose/integration-tests/demos/common/src/main/AndroidManifest.xml b/compose/integration-tests/demos/common/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/integration-tests/demos/common/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/integration-tests/docs-snippets/src/main/AndroidManifest.xml b/compose/integration-tests/docs-snippets/src/main/AndroidManifest.xml
deleted file mode 100644
index ae0be49..0000000
--- a/compose/integration-tests/docs-snippets/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
index 24e9aeb..6341378 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
@@ -48,7 +48,7 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
@@ -169,7 +169,7 @@
 private object InteropSnippet5 {
     @Composable
     fun CustomView() {
-        val selectedItem = remember { mutableStateOf(0) }
+        val selectedItem = remember { mutableIntStateOf(0) }
 
         // Adds view to Compose
         AndroidView(
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt
index 25c3edd..f75a353 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt
@@ -24,7 +24,7 @@
 import androidx.compose.material.ButtonDefaults.buttonColors
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.tooling.preview.Preview
@@ -88,12 +88,12 @@
     @Preview
     @Composable
     fun PreviewCounter() {
-        val counterState = remember { mutableStateOf(0) }
+        val counterState = remember { mutableIntStateOf(0) }
 
         Counter(
-            count = counterState.value,
+            count = counterState.intValue,
             updateCount = { newCount ->
-                counterState.value = newCount
+                counterState.intValue = newCount
             }
         )
     }
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
index 559f4b6..ab880fe 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
@@ -31,7 +31,9 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.key.KeyEvent
@@ -223,13 +225,13 @@
 
 private object SyncSnippet {
     @Test fun counterTest() {
-        val myCounter = mutableStateOf(0) // State that can cause recompositions
+        var myCounter by mutableIntStateOf(0) // State that can cause recompositions
         var lastSeenValue = 0 // Used to track recompositions
         composeTestRule.setContent {
-            Text(myCounter.value.toString())
-            lastSeenValue = myCounter.value
+            Text(myCounter.toString())
+            lastSeenValue = myCounter
         }
-        myCounter.value = 1 // The state changes, but there is no recomposition
+        myCounter = 1 // The state changes, but there is no recomposition
 
         // Fails because nothing triggered a recomposition
         assertTrue(lastSeenValue == 1)
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/integration-tests/material-catalog/build.gradle b/compose/integration-tests/material-catalog/build.gradle
index c6afe8e..6fc989b 100644
--- a/compose/integration-tests/material-catalog/build.gradle
+++ b/compose/integration-tests/material-catalog/build.gradle
@@ -29,8 +29,8 @@
 android {
     defaultConfig {
         applicationId "androidx.compose.material.catalog"
-        versionCode 2100
-        versionName "2.1.0"
+        versionCode 2200
+        versionName "2.2.0"
     }
     buildTypes {
         release {
diff --git a/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt b/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt
index dd46067..47b7cad 100644
--- a/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt
+++ b/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt
@@ -219,6 +219,69 @@
         """
     )
 
+    val Composables: TestFile = bytecodeStub(
+        filename = "Composables.kt",
+        filepath = "androidx/compose/runtime",
+        checksum = 0x92d0959f,
+        source = """
+        package androidx.compose.runtime
+
+        @Composable
+        inline fun <T> key(
+            @Suppress("UNUSED_PARAMETER")
+            vararg keys: Any?,
+            block: @Composable () -> T
+        ) = block()
+
+        @Composable
+        inline fun ReusableContent(
+            key: Any?,
+            content: @Composable () -> Unit
+        ) {
+            content()
+        }
+
+        @Composable
+        inline fun ReusableContentHost(
+            active: Boolean,
+            crossinline content: @Composable () -> Unit
+        ) {
+            if (active) { content() }
+        }
+        """,
+        """
+        META-INF/main.kotlin_module:
+        H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgMuWSSMxLKcrPTKnQS87PLcgvTtUr
+        Ks0rycxNFeJ1BgskJuWkFnuXCHEFpeam5ialFnmXcPFxsZSkFpcIsYUASe8S
+        JQYtBgDjkUhNXwAAAA==
+        """,
+        """
+        androidx/compose/runtime/ComposablesKt.class:
+        H4sIAAAAAAAA/51VzXLbVBT+rvwnK04sy05xXEhT103tGCrHpfzUaaBNKfHg
+        BiY2gWlW17IaFNtSR5I9ZZdhwzN0yxPArrBgMmHHg/AUDMORLCdO7GkK49H5
+        P+d+55wr+c9/fvsdwPv4jGGVmx3bMjovVM3qP7ccXbUHpmv0dXXL13m7pztf
+        uDEwBvmQD7na4+aB+mX7UNfIGmIIdfXvGT4v7jcuumuNruX2DFM9HPbVZwNT
+        cw3LdNTHgVSplaZTGFobrXvT9s3/UX+j3GrVNmslogw3G2/QKcXdaFj2gXqo
+        u22bG1SNm6bl8lHlHcvdGfR6FBWmph0REsPyBAbDdHXb5D21bro2JRuaE0OC
+        YVH7Tte6QfZX3OZ9nQIZbhVn9HRmaXpFDmqlvQQWkJQwD5kh0u5ZWleEcv7o
+        Ge3HkGGIGubQ6uoMmeKMaSdwBW/NYRFZBrFgFJ4V/GWyOkN6xrwZVi5bKUNy
+        Vx/4w9yyaBymy3B3VpuXXY09hkf/PW9j7P/aNFxv91Sm8LqN0kKCxce0EV4R
+        eZqWP4ypTpRZM0lfCNu2HAotFJ++SY93Lg2b1VJ2FrzRuVFOaUNaOHvKkBrn
+        PtFd3uEuJ7hCfxiit595RPQIKLbrCQI5XxieVCGps87w1/HRmnR8JAmyMGJE
+        PJ4VRo9IjzwX8JTHcyWKyQkVVhVlIRfOskqompLDuXklrJC1Ejn5KSqI0e2T
+        H8U/XjEKLckxPzwqi8Tj1bQYfl34Q6oqbEv5sHh8JEvVK/JcLqGIIlP8oyqJ
+        /PjI+VGNbWmqhqfKCyc/CDEpIp68rFaY122VeYNQxgObvNGsxbAw8Tm83aU5
+        h7esDk052TBMfWfQb+t2y3N6JSyN9/a4bXh6YIw3jQOTuwOb5Ku7o+9O3Rwa
+        jkHuB2cXku7NRe/p9+Jc2HzT5Vr3CX8eHCA1rYGt6Y8NT1kKauxN1cc6BIS9
+        pSOEJUQQJW2DtCbZvc0vrilzr5AqK2miofvlX7HE8LN3OXCfaJRmtIA4Nkle
+        oYQFxJDDVfJSKiS87ZdehIJ3KPITPy+GT4NMkfgDeuYFUuL+zfPoEpZxLcCx
+        G+CQy8qNCQTf/HIKQSIuQkYSqVMYIv0KAQyZurrpw5CRn4CxMhvG9QkYq7gV
+        wGgHMDJjGLmXkCaghPBwVOxvpNkErCzSVOcMVgKlAFYGayj7sDLnYBWnYMWF
+        U0gCtnxawyPi35L1XeruvX2E6rhdh1pHBet1VHGnTn/nd/fBHHyAD/eRdLDs
+        4CMHEZ/mHXzsQHSw6mDNt9xzIPmC4iD6Lx9Lm/oRCAAA
+        """
+    )
+
     val Modifier: TestFile = bytecodeStub(
         filename = "Modifier.kt",
         filepath = "androidx/compose/ui",
@@ -382,7 +445,7 @@
     val Remember: TestFile = bytecodeStub(
         filename = "Remember.kt",
         filepath = "androidx/compose/runtime",
-        checksum = 0xc78323f1,
+        checksum = 0x736631c7,
         source = """
         package androidx.compose.runtime
 
@@ -392,76 +455,110 @@
         inline fun <T> remember(calculation: () -> T): T = calculation()
 
         @Composable
-        inline fun <T, V1> remember(
-            v1: V1,
-            calculation: () -> T
+        inline fun <T> remember(
+            key1: Any?,
+            crossinline calculation: () -> T
         ): T = calculation()
 
         @Composable
-        inline fun <T, V1, V2> remember(
-            v1: V1,
-            v2: V2,
-            calculation: () -> T
+        inline fun <T> remember(
+            key1: Any?,
+            key2: Any?,
+            crossinline calculation: () -> T
         ): T = calculation()
 
         @Composable
-        inline fun <T, V1, V2, V3> remember(
-            v1: V1,
-            v2: V2,
-            v3: V3,
-            calculation: () -> T
+        inline fun <T> remember(
+            key1: Any?,
+            key2: Any?,
+            key3: Any?,
+            crossinline calculation: () -> T
         ): T = calculation()
 
         @Composable
         inline fun <V> remember(
             vararg inputs: Any?,
-            calculation: () -> V
+            crossinline calculation: () -> V
         ): V = calculation()
         """,
-"""
-        META-INF/main.kotlin_module:
-        H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3ApcYlkZiXUpSfmVKhl5yfW5BfnKpX
-        VJpXkpmbKsQVlJqbmpuUWuRdwqXJJYyhrjRTSMgZwk7xzU/JTMsEK+XjYilJ
-        LS4RYgsBkt4lSgxaDACMRj6sewAAAA==
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgUuOSSMxLKcrPTKnQS87PLcgvTtUr
+                Ks0rycxNFeIKSs1NzU1KLfIu4eLjYilJLS4RYgsBkt4lSgxaDAC9VMzjUAAA
+                AA==
+                """,
+        """
+                androidx/compose/runtime/RememberKt.class:
+                H4sIAAAAAAAA/61WWXPbVBT+rrzJjhfFWUgcCCFL62yV44YCdeoSAqEe3MDU
+                HsNMnmRbTeVF6kiyKW8ZXvgNvPILeCw8MJnwxo9iOFeWHW9JXdIHXZ17dO53
+                vnO/u+iff//8C8A+vmRYU/SqaWjVV3LFaL40LFU2W7qtNVX5mdpUm2XV/MYO
+                gDFINaWtyA1FP5O/LdfUCnk9DKLpRjHsJ/N1w25oulxrN+XnLb1ia4Zuyceu
+                lcps5ocxMgyHB8WHo/7sm8AOtovFTDazSS3DRv7aKo6cvlJuqBS3ljfMM7mm
+                2mVT0QhN0XXDVjrIJ4Z90mo0KGqqojQqrYbjFxFiWO6joum2aupKQ87ptkkY
+                WsUKIMwwV3mhVuouyHeKqTRVm8/K3eRocX2eAgc5y2yWwogiFkIE0mC+MaUH
+                EGfwa3rbqKsMs8kx0xrGLOamMIN5hsi6tv58/UonlmNYeZNUDMfjiP8fgQvX
+                Cfy28AOSr98kJUngKu6tqz/tMcTHEfvhZmluU7M6ec23mgVeXpqhNhnwu6nt
+                x9vUdutq7zN8nTx9R6UUD0pjS3l7fOJZcniWMs7efNmyLRFJhpkxWAzTXbin
+                qq1UFVshn9Bse+hUZrwReQPaq3VuCPTxlcatFFnVPcZeXJwnQoLoCQkLAr3p
+                kS7OyQiQMR2ijxvUTwhP2KpXvDiXWDosCQkx7o2TK+V5cvmL+PdrdnF++Ztf
+                kLyJvaFgUfIlvAss5U9HRe/1AwOJzLUDRUmcBCKY+GoCCFEKTQI2lXjcAQt3
+                wMLpeSmSCMdFkcWd4ano6ihMeBgmdvmzEAj5xMtf0ynG55s2GCvyM8TVrP+U
+                5BqV6MroXpb36jat0yOjSidzLK/p6kmLu4v8OOIQBt0tJcXUeN91Bgvama7Y
+                LZPspWedayuntzVLo8+HV8canXnDX3v3zEBYqGC0zIp6rHH0RXdMaQQPexDg
+                5esMPizS46feAfWOyC/QO7YVn3qNac/B1h94j+F3vhLxiFo/VS0igCzZ8+Tj
+                9gJBMD4IISTo/diJDuDzXjxwSE+AJgxBMnjGJTfjUwrlyzu23cn4aHtsxoiT
+                cYVCuxkdmljGh04ZndzMzf3+SO6IQJ0PnI3VZbDiMvjenYvYTodBdmcsg1mH
+                wRaFdhl4hhisknU1D4LL5aMRLjOeHpdBRmsuozIN89Fb2u0weuDdHUMpSGVm
+                nX85H9kdSrwOqUdJ6lGSsEGW4FicnMcltz5CbtE7RG6Q4h2XYsGVbW4rvkkU
+                bxIvSiy64kX7xJtDkqYTjtUv3t3x4gV7PAR84bQZWq7AKXm3idnOKTw57OZw
+                LwcZqRyt8XQO97FPARY+xoNTSBZ8Fj6xELLwqQW/hWULn1lYsrBqYcXChoU1
+                Cw8tJB3/nf8A7cabHS4LAAA=
+        """
+    )
+
+    val StateFactoryMarker: TestFile = bytecodeStub(
+        filename = "StateFactoryMarker.kt",
+        filepath = "androidx/compose/runtime/snapshots",
+        checksum = 0x79ef0e9d,
+        source = """
+        package androidx.compose.runtime.snapshots
+
+        @MustBeDocumented
+        @Target(AnnotationTarget.FUNCTION)
+        @Retention(AnnotationRetention.BINARY)
+        annotation class StateFactoryMarker
         """,
         """
-        androidx/compose/runtime/RememberKt.class:
-        H4sIAAAAAAAAAK1WXVPbRhQ9K38J29jCfJSYJiFgyndkG5q2MZBSWhpPCekE
-        j9opT8JWqMCWMlrZk0emL33pH+hrf0Ef0z50GPrWH9XpXVlgg4UJTTzW3qvr
-        u+ecu3el9T///vkXgFV8xTCtWzXHNmuv1ardeGVzQ3Walms2DPWF0TAaB4bz
-        jRsDY1CO9Jau1nXrUH1+cGRUKRpikB0/i2F1bufYduumpR61GurLplV1Tdvi
-        6rbv5UvzO1cxSgyba5XHvfGNm8DWFiuV0kZpnkaGmZ1rq9jy7vWDukF50zu2
-        c6geGe6Bo5uEpluW7ept5F3b3W3W65SVqOr1arPuxWXEGe51STEt13Asva6W
-        LdchDLPKY0gyjFZ/NKrHPsi3uqM3DFesyuxcb3FdkT0Bclia15JIIR3HIJTL
-        fAGlx5BhiJpWyz42GEbmApY1iRGMJjCMMYbBnJl7mev0iZUZJm9qFcN2kPD/
-        0+AfAhusFQK7XtEKN7Fc6rzUKjBkgmi/77/w71IRf/uKtOK1ZVa04i1LLTIc
-        vV1V76fOX96xTm2lb/EVbeWWC7DC8PXc/nuqrrKmBcq7PT6p1DyVWsl7MF81
-        XS5jlmE4AIth6BzumeHqNd3VRW2NVoheyUwMEXpGj4UjUfy1Kbw8ebUCY+7p
-        yVhckkNxaVwim4xLyhBdpyfx05PsDNms9JRNheXTE4UVk4qUlTPhDIXyoadn
-        P8t/v2GnJ2e/RSUlnF25nEyGKZFiVIlSMNJvaiy7GTSVRkmRLwCiygBZuR9Q
-        PPv8eiAaQ0qiBy6qJMkm+sEOZtfbsKk2bKo4pqSzyYwss0x4nOWH8sqUZ7tA
-        UldBMmc/SbF4RD77tZhnYu3p0WMV8abxW9f9ppS0ghiKYqAdyjRGjUTi/Px8
-        eOwyhLfsGr2s0zumZew2RbgiziSBaNNxo+mOKe794MCeeWjpbtMhf+JF+yQr
-        Wy2Tm/TzZufQYshd/fXi6LmUFt+zm07V2DYF+h1/jtaDhwIkhCE+EdyhK0p3
-        63S3RXGJbHohk3iDodDawh/4gOF3sUmxQWOUKpYRwxPyxygm/HGCYGIS4siS
-        /dzLjmHzIh/4gq4YrRkGyBGMEz7jM0oVOz+92GZcXwxkHPQYJyn1nNGTibu4
-        55XR5mY+94c93AMivcN+32f/zl+H9FKbfWMpkH3EY1+g1HP20BX2B+R11kDy
-        dUz26EiEvAkdJVO+kgNKj5BVlttKHoWXA6QMUGlPvL90EfLbUoR+5UKKciFF
-        QY48yfOEqJAvarpH1GB7K3TLmvFl7fntGV3IzJGsfk1KUS3nTUp1NWkUs5j3
-        4EcvNemjXh2Sr6A9SrQdxbiGL8nWKLpAyhb3ESpjqYxlGvGwDBX5Mm3o4j4Y
-        xwpW9zHMEeH4mCPO8YgjyvEJx12OCY5POR5w3Of4jCPHMcXxmGOWo+R9Z/4D
-        oSNh5zILAAA=
-        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgMueSSMxLKcrPTKnQS87PLcgvTtUr
+                Ks0rycxNFeIKSs1NzU1KLfIuEeIPzkssKM7ILwkuSSxJ9S7hUuOSwaVRLy0/
+                X4gtJLW4xLtEiUGLAQDM+x9weQAAAA==
+                """,
+                """
+                androidx/compose/runtime/snapshots/StateFactoryMarker.class:
+                H4sIAAAAAAAA/51STW/TQBB965AmBCgpUEhaSj8o6Q2XCsSBU9I2wlI+UJIi
+                oZy29qq4dryRdx2aW278Jw4o4siPQsw2IgnCAglZGr+ZefPxdvf7jy9fAbzE
+                AcMrHnmx9L0r25WDoVTCjpNI+wNhq4gP1Uepld3VXIs6d7WMx00eByLOgTEU
+                L/mI2yGPLuz2+aVwdQ4Zhu1FlEeRpFJfRnZ1DnPIMuw3AqlDP1qmNBOla+JE
+                uslARFp4bxg2Umg9Hl8ITclVHobyk/BmAZXedDF3Xpevn7WOe067xbCVUtER
+                msYTImp2xMNEMBz8tfNyxUrNaVU7Hxh2G6nn8Ju8nXTOcr/KPyjvZOi7Y7Pq
+                caPa7RpJqQVz9Xvp+dNQmLV646EwKpqnvbftE4a1X8KbQnOPa05JazDK0Oth
+                xuSNAQMLKH7lG++QkPeCoTyd5AtWySpYxc38t89WaTo5sg5ZbToxhCOG143/
+                enq0AQ1c/zPxPNAMha5MYlfU/ZCurdyZ9XvvK/88FIsbUxXaATeoz4rZn/Cz
+                a7uPCv0D5OgjcZS/2QcTKOAWbpN3R2AVd1Gk5NrMvYf7eGDgNW8dD/EIWZT6
+                yDgoO9hwsInHBLHl4Am2iaWwg90+LIU9hac/AcQycP6LAwAA
+                """
     )
 
     val SnapshotState: TestFile = bytecodeStub(
         filename = "SnapshotState.kt",
         filepath = "androidx/compose/runtime",
-        checksum = 0x9907976f,
+        checksum = 0xa797b7e1,
         source = """
         package androidx.compose.runtime
 
         import kotlin.reflect.KProperty
+        import androidx.compose.runtime.snapshots.StateFactoryMarker
 
         interface State<out T> {
             val value: T
@@ -473,6 +570,7 @@
 
         private class DerivedStateImpl<T>(override var value: T) : DerivedState<T>
 
+        @StateFactoryMarker
         fun <T> derivedStateOf(value: T): DerivedState<T> = DerivedStateImpl(value)
 
         interface MutableState<T> : State<T> {
@@ -481,11 +579,17 @@
 
         private class MutableStateImpl<T>(override var value: T) : MutableState<T>
 
-        fun <T> mutableStateOf(value: T): MutableState<T> = MutableStateImpl(value)
+        @StateFactoryMarker
+        fun <T> mutableStateOf(
+            value: T,
+            policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
+        ): MutableState<T> = MutableStateImpl(value)
 
+        @StateFactoryMarker
         fun <T> mutableStateListOf() = SnapshotStateList<T>()
         class SnapshotStateList<T>
 
+        @StateFactoryMarker
         fun <K, V> mutableStateMapOf() = SnapshotStateMap<K, V>()
         class SnapshotStateMap<K, V>
 
@@ -500,6 +604,29 @@
             }
         }
 
+        @Composable
+        fun <T> produceState(
+            initialValue: T,
+            key1: Any?,
+            key2: Any?,
+            producer: suspend ProduceStateScope<T>.() -> Unit
+        ): State<T> {
+            return object : State<T> {
+                override val value = initialValue
+            }
+        }
+
+        @Composable
+        fun <T> produceState(
+            initialValue: T,
+            vararg keys: Any?,
+            producer: suspend ProduceStateScope<T>.() -> Unit
+        ): State<T> {
+            return object : State<T> {
+                override val value = initialValue
+            }
+        }
+
         interface ProduceStateScope<T> : MutableState<T> {
             suspend fun awaitDispose(onDispose: () -> Unit): Nothing
         }
@@ -513,184 +640,638 @@
         ) {
             this.value = value
         }
+
+        interface SnapshotMutationPolicy<T> {
+            fun equivalent(a: T, b: T): Boolean
+            fun merge(previous: T, current: T, applied: T): T? = null
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        fun <T> referentialEqualityPolicy(): SnapshotMutationPolicy<T> =
+            ReferentialEqualityPolicy as SnapshotMutationPolicy<T>
+
+        private object ReferentialEqualityPolicy : SnapshotMutationPolicy<Any?> {
+            override fun equivalent(a: Any?, b: Any?) = a === b
+            override fun toString() = "ReferentialEqualityPolicy"
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        fun <T> structuralEqualityPolicy(): SnapshotMutationPolicy<T> =
+            StructuralEqualityPolicy as SnapshotMutationPolicy<T>
+
+        private object StructuralEqualityPolicy : SnapshotMutationPolicy<Any?> {
+            override fun equivalent(a: Any?, b: Any?) = a == b
+            override fun toString() = "StructuralEqualityPolicy"
+        }
+        @Suppress("UNCHECKED_CAST")
+        fun <T> neverEqualPolicy(): SnapshotMutationPolicy<T> =
+            NeverEqualPolicy as SnapshotMutationPolicy<T>
+
+        private object NeverEqualPolicy : SnapshotMutationPolicy<Any?> {
+            override fun equivalent(a: Any?, b: Any?) = false
+            override fun toString() = "NeverEqualPolicy"
+        }
         """,
+
         """
                 META-INF/main.kotlin_module:
-                H4sIAAAAAAAAAGNgYGBmYGBgBGJ2KM3Apc8ln5iXUpSfmVKhl5yfW5BfnKqX
-                mJeZm1iSmZ8HFClKFeJxBPMTk3JSvUu4zLkkMDQUleaVZOamCnEFpeam5ial
-                FnmXCPEH5yUWFGfklwSXJJaANCpg0ViaqVeal1kixOJS4F2ixKDFAAA4Rqdc
-                pAAAAA==
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgMueSSMxLKcrPTKnQS87PLcgvTtUr
+                Ks0rycxNFeIKSs1NzU1KLfIuEeIPzkssKM7ILwkuSSxJ9S7hUuOSwaVRLy0/
+                X4gtJLW4xLtEiUGLAQDM+x9weQAAAA==
                 """,
                 """
                 androidx/compose/runtime/DerivedState.class:
-                H4sIAAAAAAAAAIVRTW/TQBB9Yzuxk4bghhbSAKVCQk044FJxQKSqhPgQkVIh
-                NVGElNM2XtJtHLvybqIe81s48CM4IKtHfhRinKIKERUuM/Nm37zdffPj57fv
-                AF5gh/BExGGaqPAiGCXT80TLIJ3FRk1l8Famai7DnhFGuiDC0UH/VfdMzEUQ
-                iXgcfDw5kyPTPlxtdW/UXIod9PvtwzbB/3vQhUPY/vewiyLBG0szENFMEjaa
-                rdUHEArNFt/CTH3N3GyuElsDQrHJzLxY704SE6k4OJJGhMIInremc5utojy4
-                BJpw60LlaI+r8DnhXbaolq26Vc4Wy2R5Be9zPVs8dbxs4dO+V3Nq1gfas47r
-                vt2wXmaLT5dfq5dfipWG4zl+4bHjFX03F9sn7N5s3Z/r4IdRn7DzH6NzH+ZX
-                n/d7sTjXp4lZHjybGEKpp8axMLOUj8u9ZJaO5HsVMdg6vhIZKK1OIvk6jhMe
-                Ukms2XoLBRBcNsDiZXkoMXqYI5QZr6FyjW/B/l3Z2F7mB3jE+Q0zqqxyewi7
-                A7+DdY6o5eFOBxvYHII07uLekLeHusaWRkPjvs5hSWNNo/ILV3pR08ICAAA=
+                H4sIAAAAAAAA/4VRTW/TQBB9Yzuxk4bghhbSAG1BQiQccKk4IFJVQnyISKmQ
+                mihCymkbL2Ebx668m6jH/BYO/AgOyOqRH4UYp6hCRIXLzLzZN29Hb378/PYd
+                wHM8IDwScZgmKjwPRsn0LNEySGexUVMZvJGpmsuwZ4SRLohwdNB/2T0VcxFE
+                Ih4HH05O5ci0D1db3Ws1l2IH/X77sE3w/x504RC2/z3sokjwxtIMRDSThI1m
+                a3UBQqHZ4l+Yqa+Ym81VYmtAKDaZmRfr3UliIhUHR9KIUBjB89Z0brNVlAcv
+                DyDQhPvnKkd7XIXPCG+zRbVs1a1ytlgmyyt4n+rZ4onjZQuf9r2aU7Pe0551
+                XPfthvUiW3y8+Fq9+FKsNBzP8QsPHa/ou7nYPuHx9f79eRPejvqE3f+4nZsx
+                v3TA78XiTH9OzPLh6cQQSj01joWZpfxc7iWzdCTfqYjB1vGlyEBpdRLJV3Gc
+                8JBKYs3+WyiwDy4bYPHFPJQYbecIZcZrqFzhG7B/VzZ2lvk+djm/ZkaVVW4O
+                YXfgd7DeQQ23uMRGB5u4PQRp3EF9yCfElkZD467GPZ3DksaaRuUXZ2oGC8cC
+                AAA=
                 """,
                 """
                 androidx/compose/runtime/DerivedStateImpl.class:
-                H4sIAAAAAAAAAI1R227TQBA9u3Yc16Spm6ZXyq1QmqQUl4oH1EZBXFQ1UgCp
-                iSJEn7aJ1W6T2JXXifqYr+AD+AKQQEg8oKiPfBRi7EQVECTy4Lkcn5kzs/Pj
-                57fvAB5jiyEvvGbgy+aF0/A7575ynaDrhbLjOi/dQPbcZjUUoVvunLeTYAy1
-                Ym23ciZ6wmkL78R5c3zmNsK90jhUmahvsVbbK+0x2H/XJ6EzrE/UIwmDwShK
-                T4Ylhvnc+Cz5OhFyJBUFWi5fT8HENQsJpBgSPdHuugyZ8boU0piZAofNoIen
-                UjFsTrZX9F60lnnihvVh+2wuPy5A6rk8zUVMdcVMFineekq7aPGv2UrLD9vS
-                c165oWiKUBDGOz2NLsgik2RgLYIuZJRtU9R8xPBu0E9bfIlbg37suGmYfGnQ
-                L+jmoG+zHTOjZ/gB2+bPpzOGra3wJ4P+5QeD2/rh6ih9e/k+TZBtcdtc0c2E
-                bazpZtLWI4UdEq0xbEz2HNGFq544V6d+GAMPWyHDVFWeeCLsBrS0/sJvkpup
-                SM993e0cu0FNHLfjs/gN0a6LQEb5CLSqfjdouPsySpYPh4p1qST9feZ5PklI
-                31PYptslQE9KH4+OSX6DHoljGRrFJqLr5ggpkefkrcJXTBc2v2D2U8zLkzWI
-                CaoukF0YspDBHBBHv3e1KMpiftTToSyqTBQ+Y/bjP9ulhoRRu2GTBcIWrwbb
-                HQ1m/Hco42ooA0t/DKWNIg2bsb+PB+T3ibFC2tePoJWxWsYNsrgZmVtl3Mad
-                IzCFNdw9wpTCnMI9hXWFtIrSrMK8wqLCzC+1g+gKTQQAAA==
+                H4sIAAAAAAAA/41S204TURRd58x0OoylDOWOeEORtgiDxAcDTY2XEJtUTWjT
+                GHk6tBM40M6QOacNj/0KP8Av0ERj4oNpePSjjHvahqg1sS97r71mn732ZX78
+                /PYdwCNsMeRE0IhC2bjw6mHrPFS+F7UDLVu+98KPZMdvVLTQfql13kyCMVQL
+                1d3yqegIrymCY+/N0alf13vFUao8Vt1CtbpX3GNw/36fhMmwNlaNJCwGqyAD
+                qYsMc9nRXnI1SsiSVAyMbK6Wgo1rDhJIMSQ6otn2GTKj71JIY2oCHC6DqU+k
+                YtgYb654XzSWfezr2qD8bDY3KkDq2Rz1RZnqKjNZILz5hGYx+p+my2ehbsrA
+                e+Vr0RBaEMdbHYMuyGJjxwYM7Iz4CxlH24QaDxne9bpphy9yp9ftO25bNl/s
+                dfOm3eu6bMfOmBn+km3zZ5MZyzWW+eNe9/KDxV3zYGUYvr18nybKdbhrL5t2
+                wrVWTTvpmrHCDolWGdbH20l85kogztVJqPvE1plmmKjI40DodkSTm8/DBrmp
+                sgz81+3WkR9VxVGzf5uwLpo1Eck4HpJOJWxHdX9fxsHSwUCxJpWkr0+DICQJ
+                GQYK23TABO2H/l9CdFHyWVoSxxIMwjbiE+eIKZLn5J38V0zmN75g+lM/L0/W
+                okzQ2w2y84MsZDATr57Q71UdQrOYG9b04suQT+Q/Y/rjP8ulBgnDcoMi88Qt
+                XDW2O2zM+m9T1lVTFhb/aMoYIgMP+n4dm+T3KWOZtK8fwihhpYQbJdzELYK4
+                XcIdrB6CKdzFvUNMKMworCncV0irOJxVmFNYUJj6BY25tQRSBAAA
                 """,
                 """
                 androidx/compose/runtime/MutableState.class:
-                H4sIAAAAAAAAAIVRwW7TQBB9s3ZiJw3BDS2kAUqFhEg44FJxQKSqhBCISImQ
-                miiqlNM2McGNs66y66hHfwsHPoIDsnrkoxDjFFWIqHCZmbf75s3umx8/v30H
-                8BJ7hCdSTRZxOLnwx/H8PNaBv0iUCeeB30uMPI2CvpEmcECE3uHgdfdMLqUf
-                STX1P56eBWPTPlo/6t6ouRI7HAzaR22C93ejA5uw++9mB0WCOw3MUEZJQNhq
-                ttYfQCg0WzyFmfqaud1cJ7aGhGKTmXmx2Z3FJgqV3wuMnEgjuV/MlxZbRXlw
-                CDTjo4swR/tcTV4Q3mVptSzqopylqyTcgvupnqXPbDdLPTpwa3ZNfKB9cVz3
-                rIZ4laUnl1+rl1+KlYbt2l7hse0WPScXOyA8vdm6P9fBD6MBYe8/Ruc+LK8+
-                7/WVPNefY7O6eD4zhFI/nCppkgVfl/txshgH78OIwc7xlcgw1CFPfKNUzE1h
-                rDRbL1AAwWEDBC/LRYnRwxyhzHgDlWt8C9bvysLuKj/AI85vmVFlldsjWB14
-                HWxyRC0PdzrYwvYIpHEX90a8PdQ1djQaGvd1DksaGxqVX2k2HnjCAgAA
+                H4sIAAAAAAAA/4VR0WoTURA9c3eT3aQxbmOradRaBTHxwa3FBzGlIKIYSBCa
+                EIQ83SZrvM1mt+TeDX3cb/HBj/BBlj76UeJsKkUM1Zc7c+aeOTOc+fHz23cA
+                L/CQ8FhGk0WsJuf+OJ6fxTrwF0lk1Dzwe4mRJ2HQN9IEDojQOxy86p7KpfRD
+                GU39Dyenwdi0j9ZL3Ws1V2KHg0H7qE3w/m50YBN2/93soEhwp4EZyjAJCFvN
+                1voChEKzxVOYqa+Y2811YmtIKDaZmSeb3VlsQhX5vcDIiTSS+8V8abFVlD9u
+                /oBAM66fqxztczZ5TnibpdWyqItylq6CcAvup3qWPrXdLPXowK3ZNfGe9sVx
+                3bMa4mWWfrz4Wr34Uqw0bNf2Co9st+g5udgB4cn1/v15E96OBoS9/7idm7G8
+                dMDrR/JMf47N6uPZzBBKfTWNpEkW/F3ux8liHLxTIYOd40uRodKKJ76Oopib
+                VBxp9l+gwD44bIDgi7koMdrNEcqMN1C5wjdg/c4sPFjF+9jj+IYZVVa5OYLV
+                gdfBZgc13OIUWx1s4/YIpHEH9RGfEDsaDY27Gvd0DksaGxqVX4ySaL/HAgAA
                 """,
                 """
                 androidx/compose/runtime/MutableStateImpl.class:
-                H4sIAAAAAAAAAI1RXU8TQRQ9M7vdLrWUpXwjfqFIW8RF4oOBpkZNjE2KJrRp
-                jDwN7QYG2l3SmRIe+yv8Af4CTTQmPpiGR3+U8c62IWpN7MPej7Pn3nPv3B8/
-                v30H8BibDHkRNjuRbF74jah9FqnA73RDLduBv9fV4rAVVLXQQbl91kqCMdSK
-                tZ3KiTgXfkuER/6bw5OgoXdLo1BlrL7FWm23tMvg/V2fhM2wNlaPJBwGpyhD
-                qUsMc7nRWfJ1IuRIygRWLl9Pw8W1FBJIMyTORasbMGRH69LIYGoCHB6DrY+l
-                YtgYby/zXrSWexTo+qD9bC4/KkDquTzNRUx1xUwWKd58SrtY8a/pymmkWzL0
-                9wItmkILwnj73KILMmOSDOyUoAtpsi2Kmo8Y3vV7mRRf5Kl+L3bcdVy+2O8V
-                bLff89i2m7Wz/BXb4s8ns45nLfMn/d7lB4d79v7KMH17+T5DkJfinrtsuwnP
-                WbXdpGcbhW0SrTGsj/cc5sLVUJyp40jHwMNTzTBRlUeh0N0OLW2/iJrkpioy
-                DF5324dBp2aqzVmihmjVRUeafAimqlG30wheSpMs7Q8U61JJ+vssDCOSkFGo
-                sEW3S4CelD5ujkl+nR6JYwkWxS7MdXOElMhz8qnCV0wWNr5g+lPMy5N1iAnM
-                oxDbmIUsZoA4+r1riqJZzA17+pSZykThM6Y//rNdekAYths0mSds4WqwneFg
-                zn+Hcq6GcrD4x1DWMLKwEfv7eED+JTGWSfv6AawyVsq4QRY3jblVxm3cOQBT
-                WMXdA0wozCjcU1hTyCiTzirMKSwoTP0Cc/1T9U0EAAA=
+                H4sIAAAAAAAA/41S224SURRdZ2YYpiOFKb3XeqvWArWd2vhgWoJRk0YSqkkh
+                xNinU5jQU2Cm4RyaPvIVfoBfoInGxAdD+uhHGfcB0jRiIi97rb1mn32dX79/
+                /ATwDNsMWR7WO5GoX/q1qH0eycDvdEMl2oF/2FX8pBWUFVdBsX3eioMxVPKV
+                vdIZv+B+i4cN/93JWVBT+4VxqTRR3nylsl/YZ/D+fh+HxbA+UY44bAY7L0Kh
+                CgzzmfFeslUKyFApTcxMtpqAg1suYkgwxC54qxswpMffJZBEagoGPAZLnQrJ
+                sDnZXHpfNJbTCFR1mH4ukx0vQNUzWeqLIuV1ZDxPfOsFzWIOPs2UmpFqidA/
+                DBSvc8VJM9oXJl2QaeNoAwbWJP1SaG+HWP0pw4d+L+kaS4bb7w3AcGzHWOr3
+                cpbT73ls10lbaeMN2zFeTadtz1wxnvd7V59sw7OOVkfu+6uPSZI81/CcFcuJ
+                efaa5cQ9S1fYpaIVho3JdqLPXA75uTyN1EDYbiqGqbJohFx1OzS59TqqE6RK
+                IgzedtsnQaeiX+vbRDXeqvKO0P5IdMtRt1MLDoR2lo+GFatCCvr6MgwjKiGi
+                UGKHDhij/dD/S4wuSpihJRlYhkncgT5xlpQCoUHo5r5jOrf5DTNfBnE5sjZF
+                AkvYJLswjEIas3r1xG5mdYnNYX6U09eXIYzlvmLm8z/TJYYBo3TDJAukLV43
+                tjdqzP5vU/Z1UzbpN5syR8zEkwFuYIvwgCJWqPbtY5hFrBZxp4i7uEcU94t4
+                gLVjMImHeHSMKYlZiXWJxxJJqd05iXmJRYnUH5JR7zxSBAAA
+                """,
+                """
+                androidx/compose/runtime/NeverEqualPolicy.class:
+                H4sIAAAAAAAA/5VU3W8bRRD/7Z0/zhcndZyQpmn4KA3UTpqeE8pnTNoSinqR
+                ayJcBUGe1vbibnK+c+/2rCJeIv4Unnmg4qEIJBTBW/8oxOydCcFJUSLLO7Mz
+                s7/5zc7svfjr198B3MZdhir3u2Egu0+dTtAfBJFwwthXsi+cphiK8P6TmHs7
+                gSc73+bBGL5s7PMhdzzu95zP2/uiozYaL0Vo+XwQPQ7Uw1hxJQM/xamfhtjc
+                YCiNW/PIMDgXBM8jx5CrS1+qTQazUt0twkLBRhY2Q0Y9lhHDysspjxdNxGzx
+                JJZD7glfMdyonHEBpyzVrxmWGkHYc/aFaodc+hEV4gcp08hpxp7H254gdMYZ
+                yqcRyNFmsFTQUqH0ewyzleqJqNRKUdf/L0ugdCJ9t+NlWbjMkO2LsCcYts9V
+                0xlVnkW7fsGGLX0qvuGxp9z+wIvyuMrwXeWiE3UutufiX8QVvGpjEa/R5Q9C
+                MZRBTAOT78RhmPQ/zwcDT4ouuesdLxkzG4aeLcttth7da27dL+ItTBbI+DbD
+                dOMgUBTmPBSKd7nidENGf2jS62N6sfQCavYB2Z9KvauR1l1jEEeHi7Yxb6T/
+                0tGhbVhamSBpkpyy/vzemD86XLfLuXJmntWMGvskbxl//JAzSub2YimzYNSy
+                61Ypt5B4Scun2oO5batkkbfwYE4nW2dYu/CVM027eLJ5NGb/xLYoVNw6oPu6
+                +kUK4/pDGUka+Xv/Dii9x62gSwN4qSF90Yz7bRE+0s9Cv4igw71dHkq9HxmX
+                xrF2eMj7QonwP6BF1/dFuOXxKBK0LbRkz+cqDgnBbgVx2BGfSQ13ZQS3e4oY
+                atS9LNVHnzwaCGonyfepNzmScySz5KevCu0+oJ2jG6ity88x8YwUAx+OgoEQ
+                H9FaTANITib9nsIlitKH79JOt90wfxw7GSUn30i9o5Nam0Y58RuYIc3ABumT
+                BplKCXS6zuKVUYLVETuzPP/TWAZ1gps5ynCyTF38Ank0zA4y9KOJXV65ufoL
+                Xh/HSutcTmOO2VrEP2Vr4VrCVmtvkmYeZ9KEdQ0z5lgNpST4+vE9X06OAxO/
+                wfjqOZZ+xo1niSGDOq02hS1Sy94l1h8n7ExsJvI93CHpkr9CPajuwXSx7GLF
+                xU2skopbLjWxtgcWYQ3re7AjTEZ4J8LtCIUIU4kyHWEmwizpfwOnXXg5QAcA
+                AA==
                 """,
                 """
                 androidx/compose/runtime/ProduceStateScope.class:
-                H4sIAAAAAAAAAI1T328SQRCeXSh3INUr/gJarVqNSox3Ep+EEI2GFEO1EfSF
-                p+U4cOHYJbd72Efin+KDf4PxwRB8848yzkFpY7G2DzezM/PNN/vju1+/v/8A
-                gKewQ6DARCeQvHNgu3I4ksqzg1BoPvTs/UB2QtdraKa9hitHngGEQLPcfFbv
-                szGzfSZ69tt233N1qbKaqp9KvBdq1vYXxOVms1QpEbBO9hsQJ3DvXBwGJAik
-                2SfG9SuuIhhu80F9ILXPhd0fD+1uKFzNpVB29XDllJZ1VwYy1Fx4yn4pkVyE
-                LAKUHq4eiUD3LNrysv5ecLyWs6aUC5V/D7pbl0HP7nu6HTCOA5gQUrPFsDeh
-                70eHR9jO/2BSR0hEbSx3sedp1mGaYY4OxzHUAImMQYAMMHXAo8jBVecJnnU6
-                2UrRLE1NJ0fOIsuIWlHG7Gank0LcnE4sUjQz8QzdJQ59vW3F8tSJF9PWWn6e
-                dQwnsTv7+vznNzKdzL4kqGXOPtN4ipq5aFqRwKPT9bIiRNw+aRK4fz6JIRoI
-                JKU4EkdmeR/HakAFNgQbqY9Sz5seDzT2NHhPMB0G2LP5bkFdE2OuOHK/OL5q
-                fK6T1X0WsKGnveAvWKohw8D1qtxHxtxhz4cVPpQzhTXctBG9EP4HJiQhBjcx
-                opCCbfQJrF5Afwu/dYpBOoLO7RIYg9tzfwPuoK9idR1JL7YgVoNLNbDQwkZk
-                MjW4DFdaQBRchWstSCq4riCrIKfAVJBXsKlga75I/gEnsHRUOwQAAA==
+                H4sIAAAAAAAA/41T328SQRCeXSgcSPWKv4BWq7ZGJcY7iU9CiEbTFENrI+gL
+                T8tx4MKxS273sI/EP8UH/wbjgyH45h9lnINeG4u1fbid2ZlvvtnZ/e7X7+8/
+                AOAZbBMoMtHxJe8cWo4cjqRyLT8Qmg9d68CXncBxG5ppt+HIkZsEQqBZaT6v
+                99mYWR4TPettu+86ulxdDtXPJN4LNGt7C+JKs1mulgmYp+uTECdw/0IcSUgQ
+                yLBPjOvXXIUwPObD+kBqjwurPx5a3UA4mkuhrJ0jzy5HeUf6MtBcuMp6JZFc
+                BCwElB8tj0Sgex5tJcq/Fxyv5bwulWL1342269LvWX1Xt33GsQETQmq2aLYf
+                eF44PMK2/geTOkQiai06xZ6rWYdphjE6HMdQAyRcjHABAmSA8UMe7mz0Ok9x
+                4OlkI01zND2dHBuTRDtqhhGjm5tOinFjOjFJycjGs3SX2PTNphkrUDteypgr
+                hXnUTtqJ3dnXFz+/kelk9iVBTWP2mcbT1MiH3UoEHp8tmiU14gykSeDBxXSG
+                aBwwJcWxQrLRpZxIAmXYEGykPko9L3oy0FjT4D3BdOBjzfq7BXVNjLniyP3y
+                5L7xzU5nD5jPhq52/b9g6YYMfMfd4R4y5o9qPizxoaYprOChk+Hb4M9gQApi
+                sIk7Cmm4gzaB2Uto7+K3SnGTmT9juEbAGNyb29uwhXYHs6tIerkFsRpcqYFZ
+                gzXIogtXa3ANrreAKLgBN1uQUpBTkFdQUGAoWFewoeDW3En9Ae+dQkpABAAA
+                """,
+                """
+                androidx/compose/runtime/ReferentialEqualityPolicy.class:
+                H4sIAAAAAAAA/5VUW08bRxT+ZteX9WITYygBQq8hiblljUuvuDSUpoqRIShG
+                RC1PY3viDKx3nb1YifqC+lP60L70oVEfUrVShdq3/qiqZ3Zd6hqoQJbnnDnn
+                zHe+M+fM/vnXL78BWMUGQ5k7Lc+VredW0+10XV9YXugEsiOsR+KJ8ATp3L7/
+                LOS2DF7surZsvkiDMTyuHfIet2zutK2HjUPRDNZqF0LVHd71n7rBdhjwQLpO
+                jFM5C7G+xpAftqaRYLCuCJ5GiiFVkY4M1hn04vx+FgYyJpIwGRLBU+kzrF5M
+                +cLqiaEpnoWyx21yM9wpnnMTZyzzXzHM1VyvbR2KoOFx6fhUkePGlH1rJ7Rt
+                3rAFoTPOUDiLQI4GgxG49cCTTpthojg/EBVbKerm/2VxA5WIoqYvrM/AdYZk
+                R3htwbB1qeLOKfc8/pUrtnDuc/GEh3ZQ7XRtP40bDF8Xrzpjl2J7Kf5ZTON1
+                E7N4g7rQ9URPuiGNULoZel40CGne7dpStMhdadrR4JnQ1LQZ1Z363sbO5v0s
+                biGXIeNthrHakRtQmLUtAt7iAacb0jo9nR4mU4uhFlDXj8j+XKpdibTWCoM4
+                OZ41tSkt/udPjk3NUMoISZ3kqPHHN9rUyXHZLKQKiSlW0krss7Sh/f5tSsvr
+                W7P5xIxWSpaNfGom8pKWjrUHk1tG3iBv5sGkSlZmWLnylTNFOzvYPHrU/8TW
+                KVTcPaL7uvEohqk6PelLmv2NfyeVXuim26IBvFaTjtgJOw3h7an3oZ6G2+T2
+                Pvek2veNOYJtHm3zbn8/N4y9yz3eEYHw/pMkW3Uc4W3a3PcFbTN12XZ4EHqE
+                YNbd0GuKL6SCm+7D7Z8hihJ1M0n10keRBoTaS/ID6lWK5CTJJPnpu0O7D2ln
+                qYYq68IrjLwkRcNH/WCA42Nas3EAyVzU/1Fcoyh1eI+kGoORxaXvkE58j4T+
+                A211rMV5MvfYAFojQnsrPtFHU9oYCpF/BOOkaaiQntPIlI/SxesEXusnXe4z
+                1gtTPw7xbQ7w1fsZBktXFzJDHgWziwT9aKoXFpeWf8abw1hx7QtxzClbg/jH
+                bA28HbFV2juk6aeZFGFVw7g+VEM+Cr55evfXo+NU+K/QvnyFuZ9w52VkSOAT
+                Wk0Km6U2vkes1yN2Oj6N5Pu4R7JK/iJd8/wB9CoWqlisYgnLpOJulRpbOgDz
+                sYLyAUwfOR/v+lj1kfExGiljPsZ9TJD+NyU9ROd/BwAA
+                """,
+                """
+                androidx/compose/runtime/SnapshotMutationPolicy$DefaultImpls.class:
+                H4sIAAAAAAAA/5VSTW/TQBB96yR1GlL6wWcotEADohwwlTiRqBIqQjJKS0Wi
+                Hspp4yzpJvautV5HRfwpzhz4AfwoxKyTCmgQIpY9++bNm5n17H7/8fUbgBd4
+                zNDmamC0HJwHkU5SnYnA5MrKRARdxdPsTNvD3HIrtTrWsYw+NV+LjzyPbZik
+                ceaDMayN+IQHMVfD4F1/JCLro8RQSYQZCobPTzoLdmh1Lhf8L2Z3nmI4bfde
+                zvP7C++p3eu19ltkpu8ufQzNjjbDYCRs33CpsoArpadJWXCUxzHvx4JkO/+S
+                aeuUpKo07ZnMGPYWHhdDNTViInVO6Rt/m4If5cYIZQnxNI2lGDCsd8baxlIF
+                h8LyAbecdF4yKdG9YM5UnQEDGztAB+qdS4eeMwQL7tBHg6H++72hS3Mh7ZJS
+                PBvT3soHekAXZrUjlTjKk74wPTdB90864vEJN9L5M3K5K4eK29wQ3nw/7R+q
+                icwkhV/9GjEd0+XoMTc8EVaYP2T1UClhDmKeZYLcWlfnJhJvpGvWmJU4mSuP
+                PXgoYzqrZVSwRN498t6S73iPfXEWW2SXiANOsU326TSKGq4U2R7qWCniHq4S
+                8gq0SqiE+wX28WBWo0rrQ/pWykXTi6eEHbI14jzcwSYaaBaJd/GI1i3i10iz
+                /gGlEBshroW4jhshbuJWiNs/Ac089AgUBAAA
+                """,
+                """
+                androidx/compose/runtime/SnapshotMutationPolicy.class:
+                H4sIAAAAAAAA/5VTzW7TQBD+1kltx2mLC6WkKeU3NCkHnFYcEK2QEAjhKgXU
+                RBzoaZNswybOOnjtqHDKs/AYHFDEkYdCjJ1WINIDlbzz/83szox//vr2HcBj
+                bDN4XHWjUHZPvU44HIVaeFGiYjkUXlPxkf4YxodJzGMZqndhIDufLTCG2n7r
+                aaPPx9wLuOp5b9t90Yn3ns2bGNx/bRbyDI74lMgxD4SKGaq1eeC8ZfsDQ6HW
+                au3Rl8oLQxH1BMPBf6EvyHfRbZfPCqQ16DBUGmHU8/oibkdcKk3dUuGsHdp7
+                kwQBbweCwlYagzAOpPIORcy7POZkM4bjHHWZpcROCRjYgOynMtXqJHV3GL5M
+                J6uOUTKc6cQxXCIZLzqGbdsnpenkYd6eTly2a9SNg003Vzbq+V3TXSgbT6YT
+                EsxMeL3146tpuNZB1bXL+RKb+QrnQc65UJxFr2XRi+kNdhl2GpfcAXoca7Hs
+                PZxOm8EeRWIsw0QzWJ0kirK5Wnw0CqToMuxfskDlpTjhSRD7w1GgLdxhWPzb
+                Qlt1jmsSTDwaULVCU/YUj5OIdmLjaJbeV2OpJY3o+Z+xUS5fKRG9CLjWglSn
+                GSZRR7ySASHXz5Dv53AmdQsL6Rhh0AaboD8BlVSDTXoBDkkzvYgHxE3yLxLP
+                Y4uoQ9pd8t3D+llUDtWM30eN+BH5lyj/8jFyPq74cH2s4CqJuOZjFdePwTTW
+                cOMYSxoljXWNsoaZ0Q2NmxqWRkFjU+OWxm0N5zfYvY915wMAAA==
                 """,
                 """
                 androidx/compose/runtime/SnapshotStateKt$produceState$1.class:
-                H4sIAAAAAAAAAI1T3U4TURD+znb7w1poqYCAf6gVtkVZICZqCiSGSNJYNaGk
-                MeFq2V3Kge1Zsnu24bJP4QP4BJpoTLwwDZc+lHHOtjEoCF7s/GXmm2/OzP74
-                +e07gCdYZXhqCzcMuHtiOUHnOIg8K4yF5B3Pagr7ODoIZFPa0nsly8dh4MaO
-                l7jllSwYFTcO7a5t+bZoW2/3Dj1H1hr/xlOFazs7tY0aQ/Hvwix0hjuXF2eR
-                YcisccHlBsOkeb57pUUJJvVQRsqstPLI4ZqBNPIM6a7txx5D6XxdHmMojEBD
-                kUGXBzxieH7JJJe+DE03WlYcue23Bh1zbU8OzQmzcr49cTMrxJo4J3K8cRRI
-                nwvrtSdt15Y2xbRON0VLY0pkGdgRhU648pbJclcY1vu9UaPfM7RpzdByepX1
-                ezljut9bzZX0kvas31tm21NFbVaZ707f66cfMoahFdOzei5V1BUI3cPcFQsk
-                Jub/PkwWZYb82ddh2L9gaxdEhvMfdjvWfiwcyQMRWVtDa7VWuYplHvNYoDP7
-                g9HSkWQYafK2sGUcEhl9M3BJFRpceG/izp4X7th7fnIigaO2F3LlD4P5uhBe
-                uOnbUeTRgRReCscPIi7atKWDwGUwmkEcOt4WV9kz2wNCLR5xKn8hREAc1BxY
-                oUNLg4H+IZTU5ZGu0iI1TNMHOll1iotkbZFWEaP6FaPVxS8Y/5TkPSI5BrX8
-                eehYoPx5PCZvapBNqNeBxJo4g26QNZnkKGyLPEY6Xf2M8Y+/YTNJcCGByw8S
-                hnADkBvkLyXQLGkGzBAUiMZDmMOcFJYTXaFRgXXKnKGq2V2k6rhZxy2SuK3E
-                nTruYm4XLMI93N9FJlLmgwhjESYjTEUo/AJAwj8ArQQAAA==
+                H4sIAAAAAAAA/41T0U4TURA9d7ttl7XQUgGhKqJW3RZloZqoKZAYIklj1YSS
+                xoSnZXcpF9q7ZPe24bFf4Qf4BZpoTHwwDY9+lHHutjEoCKbpzLmTmTPn3pn9
+                8fPbdwBP8JjhqSO8MODese0GnaMg8u2wKyTv+HZDOEfRfiAb0pH+K1k8CgOv
+                6/rxsbiSBqPi+oHTc+y2I1r2290D35XV+r/5VOHq9nZ1vcqQ+7swDZ1h/uLi
+                NFIMqVUuuFxnmLbOdi81KcGiHgokrFIzAwNXTCSRYUj2nHbXZ8ifrctgAtkx
+                aMgx6HKfRwzPL7jJhS9DtxsvKo3caTeHHY2WL0dwyiqdbU/arBKpJs2xnawf
+                BrLNhf3al47nSIdiWqeXoKExZQxlwMAOKX7M1WmZkLfCsDboj5uDvqnNaqZm
+                6GU26Bvm7KBfMfJ6Xns26C+zrZmcVlDw3cl7/eRDyjS1XLKgG4mcrkgqDAuX
+                TJHkWP/7OmncY8icfiKGvXNGd05k9AgHvY691xWu5IGI7M0RqlRLl6nM4AEs
+                2rU/FC0dSoaxBm8JR3ZDEqNvBB65bJ0L/023s+uH285uO96TwFUjDLk6j4KZ
+                mhB+uNF2osinLcm+FG47iLho0aj2A4/BbATd0PU3ucqe2xoKavKIU/kLIQLS
+                oO6BFdq2JM2QPiTk1fqRX6RBapilP80Yah8fEtokryJm+SvGy4tfMPkpzntE
+                dgJq+EvQYVP+Ev2AmWE2sV5Va0Jo6hS7SWg6zlHcttoi8snyZ0x+/E2bioN2
+                TJcZJozohiTX6GzH1CxuBsxhmayO+yiNchJ0ReXLqJBfo8w5qirsIFHD9Rpu
+                1HAT8wRxq4YF3N4Bi3AHd3eQihQsRpiIMB1hJkL2F+yJDPSyBAAA
+                """,
+                """
+                androidx/compose/runtime/SnapshotStateKt$produceState$2.class:
+                H4sIAAAAAAAA/41TUU8TQRD+9nptj7PQUgEBFVGrXotyUE1UCiSGQNJYNaGk
+                MeFpac+y0O6Ru23DY3+FP8BfoInGxAfT8OiPMs5eG4OiwMPNfDuZ+ebbnbkf
+                P799B/AEjxmectkIfNE4dut++8gPPTfoSCXanluV/Cjc91VVceW9VLmjwG90
+                6l50zBWTYFRcOeBd7ra4bLpv9g68uipV/s+nC1d3dkrrJYbM34VJmAxz5xcn
+                kWBIrAop1DrDpHO2e75GCQ710CDm5GspWLhiI44UQ7zLWx2PIXu2LoUxpEdg
+                IMNgqn0RMjw/5ybnvgzdbjSnNQreqg06Wk1PDeGEkz/bnrQ5eVJNmiM7Xjn0
+                VUtI95WneIMrTjGj3Y3R0Jg2ljZgYIcUPxb6tESoscyw1u+N2v2ebUwbtmGZ
+                BdbvWfZ0v1e0smbWeNbvLbHtqYwxq+Hbk/fmyYeEbRuZ+KxpxTKmJikyzF8w
+                RZLjXPZ1krjHkDr9RAzH/xjdpSLDZznott13HVlXwpehuzVExVL+It0pPIBD
+                2/eHxsVDxTBSFU3JVScgeeaG3yCXrgjpve6097xgh++1os3x63qogdDnYTBV
+                ltILNlo8DD3am/SmrLf8UMgmDW/fbzDYVb8T1L0tobNntgeCaiIUVP5CSp80
+                6HtgmfYvTlOlXwtZvZDkF2i0Bqbpo6lDb+hDQlvkdcQufMVoYeELxj9FeY/I
+                jkGvwwpMlCh/BYt0mhpkE+tVvTiEJk6x24QmoxzN7eq9Ih8vfMb4x9+0iShY
+                iuhSg4Qh3YDkGp3diJpFzYAZLJE1cR/5YU6Mrqh9AUXya5Q5Q1Wzu4iVcb2M
+                G2XcxBxB3CpjHrd3wULcwd1dJEINcyHGQkyGmAqR/gVj7U/wxAQAAA==
+                """,
+                """
+                androidx/compose/runtime/SnapshotStateKt$produceState$3.class:
+                H4sIAAAAAAAA/41TXU8TURA9d7v9YF1oqYCAiqhVt0VZKCZqCiSG0KSxakJJ
+                Y9KnpV3Lhe1dsnvb8Nhf4Q/wF2iiMfHBNDz6o4xzt41BQeBhZ87czJw5987s
+                z1/ffwB4ijWGZ45oBT5vHdtNv3Pkh64ddIXkHdeuCeco3PdlTTrSfSVzR4Hf
+                6jbdKMytJcGouHrg9Bzbc0Tbfrt34DZlqfp/PlW4vrtb2iwxZP4tTEJnWLi4
+                OIkEQ2KdCy43Gaats93zdUqwqIcCMStfN5HCNQNxmAzxnuN1XYbs2ToTE0iP
+                QUOGQZf7PGR4ccFNLnwZut14Tmnkjlcfdky1XTmCU1b+bHvSZuVJNWmO7GT1
+                0JceF/ZrVzotRzp0pnV6MRoaUyalDBjYIZ0fcxWtEGqtMmwM+uPGoG9os5qh
+                pfQCG/RTxuygX0xl9az2fNBfYTszGW1ewXcnH/STjwnD0DLxeT0Vy+iKpMiw
+                eMkUSY511ddJ4gGDefqJGNrnjK5xzi6NXuGg17Hfd0VTcl+EdnmEiqX8ZTJN
+                PIJFy/aXpOVDyTBW423hyG5AavQtv0UuXeXCfdPt7LnBrrPnRYviN9UMA67i
+                0aFZEcINtjwnDF1ak/S2aHp+yEWbZrXvtxiMmt8Nmm6Zq+y5naGgOg85lb8U
+                wicN6h5YpXWL0xDpT0JW7R/5JZqkhln6aMhQC/mYUJm8OjEK3zBeWPqKyc9R
+                3hOyE1DT34ZOWWPklymaGWYT63W1J4SmTrEbhKajHMVtqzUiHy98weSnP7SJ
+                6LAc0ZnDhBHdkOQGxXZEzaJmwBxWyOp4iPwoJ0ZXVL6AIvkNypyjqvkGYhXc
+                rOBWBbexQBB3KljE3QZYiHu430AiVDAXYiLEdIiZEOnfCWOj1bMEAAA=
                 """,
                 """
                 androidx/compose/runtime/SnapshotStateKt.class:
-                H4sIAAAAAAAAAKVYW1PbVhD+jm1sIxwQBgKIBEjiBDAXG5ombXBJU3LB5drg
-                0KY0TYQtQGBLro5MkzemD33sj+gvaJ+SNDMdJn3rT+mP6HSPLBvb2AZSz0jn
-                tvudb/fs2dX473//+BPATRwwjKpGxjL1zMtY2szlTa7FrIJh6zkttm6oeb5r
-                2uu2amuLdgCMQd5TD9RYVjV2Yqtbe1qaZr0M7RnN0g+0jCO5us0wO7pUKzg7
-                ttRwp/sV6rMMjxKpOyf150ZTqbOCJEh0jpCuLZnWTmxPs7csVTd4TDUMk9Z1
-                k/orpr1SyGZJauxMmMlcPhtAK4M/oRu6PcfQU8/KjRDaEJIg4QLD9TMhB9DB
-                0HKgZgsaQ/gkJjk4V7DVraz2oQ5erlD/YAdXgpQc3Nh1ldJF112U0NvcKZU6
-                AfSTMyrtXtK5LWyfGm1CsipohQaRTDYw9zwwJYOjZ1cJYJDBO1qMiGEJQ7jC
-                0Flp0bKaFwZNnpkJKRCH54nFOvZs/C8bCTmRWpxNbZxyrLVKAdyQMCIsC+Ut
-                M1NIFy1j2K4ToXVm9k07qxuxvYNcbLtgpItX86Hbm2kWjqVg/qdxMJ97v8Rk
-                4/3WKsxbT5t59w5MliDTpmUWbN3QeGzeJBWj4CSaRFngCWUNUhivQ/ZUO0vh
-                d72x3LwzFrFFcpFmiY+ynisWdM/MCmKKYbDCObpha5ahZmNJw7YIQU/zAOKU
-                89K7WnrfzZ1rqqXmNBJkGGl+3OsCZMfJjjP4SMI0bjLcPmvpiVSGVmQ6gFsS
-                botUMtjcawF8SnEpsrWuZjeK+dW3r72aZhg+LfLIOTua7Sq9GD0tDhuHmqVt
-                Z2kcW6QAymuW/YoOu06KzzcI49MCY9yJjHNsn4hSuJGSKGT2rs7ngrhXjARn
-                OYh5hu7ROhxDmMODNtzBQ4YLET2yHTl2EEtSkYoIuIrJ4dMvb0CoED6D0thf
-                xI6XQXebeKSqzJ3nROrVcYYfz30kJ0vkOQ9GVGC6IysItUHBVyVHH5vvOvl4
-                YuTMVb+ztPOyZqsZ1VZpzpM78NKXIBOvAB3jvuh4aP6lLnp04T2ZacbeHB2u
-                SEeHkqfPI3mCPqctD71u6zluZVosrZce2VOlTnrKEMkpwbAv7FnwxNlVX/Do
-                UPbM+GWvQhPvf/V7ZJ8SllvKIn5XRBmUA0q7M9nqvKV4sLjUSg2TJUJuK2uF
-                6gFfUJ7J7WWRjmMRWYjMBOVOxdfH4uGZSblLGQuysBQuCfeU+vHe+MWwP+zI
-                xbsFbLBvIfDXG3Z06OzRr9yUFQHnokfL2xH+gIsvyZcUf5i8Eb+88P5nyVEc
-                VBLykEL4tYqh+ooly8oAw+9/8pCbg/3i/GaaBknNtzdLMUyc7/Nq/FwfL2yR
-                HrpcEJ+6bkBWZt0mm5+owLMVebxB+SWRwZLIg5e2RpXMNEr7pV45GHIVzal9
-                m6rEvJmhy9WxRIArhdyWZqXEVRKczbSoJZYuxu5k67q+Y6h2waL+wOMi26Rx
-                oHOdlu8dl16qy7Wr5RJaJRZKGoZmzWdVzjUaSutmwUprD3WxWb8LsXECnqqq
-                Bz6IH309owV+eJGj0S1qyeMIvYP0NPoa7UeQfxMXHQa9/c6aDFNIFOXQiTC1
-                eUcmgB9cqSC1/ehC90ncXoHbdwSlFrevIW5PDe4ALrm4w7QqfsF3GHr6GlcF
-                JqvAVFyEyzUI1xA5iTBCCKO1CJddhOs1CGOIkgcFwhohiRQYngjH3uDjd7gt
-                LPzkCHeqLfTjhmPhcFEas46Fopeghzm9KXxGGsUdx50dJepNCH70WPR0OFkY
-                k867SGUOd11jnrnH2hMNf0FUJsL36e2di77FI4ZqNu2IOWyEFe10cgtIOrx6
-                8CUWHV49WHJ59WAey2Venzs8urzu/tVcVrDqcsmRaAu1vVVcbvmik2/x2IPf
-                y2yEhR0UIRfpS810/mxooXEQ68RIGNOLFJ44jHrLjHpdRqInIsXrcltzuPX4
-                6nADCXFnO/qedMb9sB2lLArU/kLzG7T115vwJvFNEk/pjW+T2MR3SfLs95tg
-                HM/xYhNXOFo4VI4tjk4OP8cAR5rjGkeGQ+PY5ujieMLRzTHGscgxy5Hg2OGY
-                4tjl0Dn2nOE+R5RjjmOJY55jmeMuxwrH6n8UZ3xFehEAAA==
+                H4sIAAAAAAAA/91Ya1cb1xXdV4AkZAHjsTEgO0S2IWAwCLCT1IY6dokxCg9T
+                g6mJ6ziDGGCMmFHmjohpm8RNW/f9Th9pkz7Sd5u2br/EqdfqYrnf+lPav9DV
+                9tyrkRBiRiC7WcurH6R758557LPPua/5+7//8lcAJ3GHoVMzF2zLWLiZSFmr
+                GYvrCTtrOsaqnpg2tQxftpxpR3P0MScExqDc0Na0RFozlxIX52/oKRqtYqhf
+                0G1jTV+QkhcXGQY7x0sFB4+N+3p6tkh9kOHC0Mzp7fpnOmdmdmtkiETPkKWn
+                /cW5Gx1PSI0RLeVY9vqEZq/oNmkeHbfspcQN3Zm3NcPkCc00LZIzLOpPWs5k
+                Np0mqWO7QpNczaRDiDAEhwzTcM4wNHrxMxtFFHUR7EE9Q/uuLIegMNSsaems
+                zqBut0mpWc062nxaL6Qm7eHan6V8DUxkc8FPWWkjtV4uDRNF/sj/K/65rNRr
+                Lqm7dJ2vgGBG6obRxNC6Yjlpw0zcWFtNGKaj26aWTiRNx6YEGykeQgtlJrWs
+                p1bcDE9ptraqkyBDhxdtmyPTwsiSzOFBHIoghsfKVUcx0lx1PB5BvHzei3VC
+                OMLQX3HaGA5sLYe2BX1Ry6Ydhtf+F2WRrGjWl1RKM3fsbMrJ2lr6/EtZLW04
+                6zmzDCc6y5jxwRJFBzojCOBYFPvRKHrdNEeK4x83uCOmRO9uzBc0CGrSp6gr
+                MZMv0K7dq4TQx1DVmVsoBiLoxwmGvcURTWgZEVDPrpGQAmG4PjTmEc/sQ8VI
+                lodmxgZnZmWU/lOhVCmED0VwSkQWzdjWQjaVi4xhsfwMzI8UzfHFrJnKrdgj
+                bm+gXD3mC/EfZZasSv0N9fj7myoKbzplZdw1qydvMmXZVtYxTJ0nhi1SMbOy
+                vocKApdpMyGFbg+wO8aZL792f7lh+Sxqi+Tayu2HtFS6YmE3Z3YYZ2n73e3Z
+                oq041W39IXwkgmGxHLaWjyKE81QnYlM1tPRsbhusXtHX+xniO1UCw83dVdQH
+                UWP/qqjG/u+q7gELYyCE8QgmRGGILA8wLHmk8OoHkrF/+mescoePfIIEvTyM
+                Sw+cqhMhzERwWaRqnwc/tFIs6Y47Y1/s3Il+f4JtfTFNz4kxoi2j2444m3p4
+                y/hkbyc6uiUfFbgf6iKSSUkc9p1lg58J42puWZSvw7jGsL/TA2MUV3B9D0bw
+                IkNdm9G22LZJEEvSQb5NmCsajO9csyGhQvYZYv58ETpeMLpchpEt57VKMuJ1
+                12F4ueKUbD/fV5gYcYGkw9My6vbgKFbyRG+G75K8OdCx6+NrC3nUbd0UO1Hp
+                +XXywc+LXlcghgFfvUt+KEKgW0w4OTk9c25ymHbNk/6+fW1QkTrI1oJjjeFt
+                k7b9eEoeBOLzOvW4E3esuGmZPfKVs57R43knva6TXtdJr1+AcWtxZyV3yen1
+                5Zym3U06GRRf5qL4RO5m9kmGRIW8h/AK3bfKzDfvm0sIr9Hlpdw89daTPH+6
+                FrfwOsNbjwDPflczovmzDIqpr+m2fJOveP/z/mSJbAifZ+j256hUXnLzxVp8
+                AV9ieOMR4KY0eOLkK3Qtyy9EE7qjLWiORrM2sLpWBYCJv7D4Ay3tK6IToJc3
+                DdGjG15goZ8F/rNx64XIxq1IoDkQCYSrZVt4rPJ6DITpp4hB0Wku+imBTUFl
+                L7XB/HPsMCnEwmq1GhgN9LEj1eGNW0pgIKhUxWhgNHT/nWBAqY7tV2oKQkFX
+                aDQUiyuhWL0crpX/kb5w7mUtNUyJkEi/sqegGS0xP1AfDih1hdf1rruGmKoo
+                m6OuUmxJ2VsYVDct7ZOWwsr+WHUz62uk3gG316M0xY6FmRpR82ot+X7fwb6Y
+                GlSlXF+z8Bo+NBr62122cUtCeCx2bXfeHsJHa2ylnI8DyuOxqBom2zkL8SMP
+                7fEwEXtkO7GqcnT74EmlTcBwH7sKMCn2djf2iPJELKhSEfV1jN6/HZEuOmND
+                yrEYISlVjHorFiotb6Dr/usBKvZwi5gGA2X335KPxmyG4Xhln3HKrDoeH0nY
+                GP3o3ALxpdWd3MW3yTLOtx3pB4vupz7neRJpzYucv+noJqfBvL+ZdWlD2QKz
+                d8Whg/uwtUDnloZxMjiZXZ3X7RlxShGYrZS4I9uGeHYHa6eNJVOjhZ36By/l
+                0CbNNYMb9Prc5hWf7v+lbwvfRreI1RGS1AoR5jqIJk1Tt4fTGuc6vY5MW1k7
+                pY8Y4l2La3J2mzv0I4BqsUCiDi2oQRBV+CU9PUUtZQDRe9gz1/UeGjaw945Y
+                P/Er+g/Kdyp+LSRycvS0j9rfSJkQfkut+BIYpraWfi3i0yB5ErZHyHZA6Her
+                zXfReg9x4eHwBo5u9RBEq/RwICftehC9JrTR+5yvA9t8/U60Abno06NSi3Y8
+                QX3h+pQbrnKo5tU3EbqLrvGu7rs4nnP8Lv1T2BGJoB5ilzhIKB4j6wcpgh70
+                uuzEJSLaW+6hf+49nLwjnW0yc9jFlvDg4UnJbYmVU2TldKmVdtfK0x5WBjHk
+                sjlF1gRS9bh6TrI5LNh8dgMjpWz2SjbjOWlcKLA5Sr1cPs8iSRo5rx8ueD1T
+                xGuD3FfxTJ5dCeU5F8rzZFqwq/a4UCYElEkPKKcllK6ctCeUi9QLFEBVuaDG
+                PEE1VheB2gptyoU267LU3K1OE7Q8V5cFwNltAKM4X8RVcwFgMy7hYxJg8xau
+                PlqOq9oiQFcw5yb/mluJjV3qxyWgF+i/6kzX+9AYtqKpx2SBrnqaW/NISVyN
+                WIAucTVi0SWukQwvFXA9L3HsqyqgKCZnGYaLZZVEa6ht2oLlqequnveRDuBP
+                BTQiwgZcpjk3KxGdJLUGqspVQiSCaYIJSyJqKiBqchGJnpjB+VTeyKduOzaB
+                LoNOF127DJYWqD/j5Tl1/S4+tYFXS+fKddfqSzITwUKQHV5mPjOnfs7bzLKP
+                mdteZr48p37V20zG00wVfk//MWqH3SAn3Pay2/5Bav0Cf6T2XdL6GvH79auo
+                SuIbSXwziW/h20m8ge8k8V187yoYx/fx5lV0c9Rw/IDjhxwqR5Cjh+Mtjic5
+                3ub4EcePOfZzWBxNHG0cjRy3OQY5dI4LHKMcP+G4yHGW46cc73D8TI78nOM5
+                jiGOS/JxiiPD0cFxhWOR4xrHEsccxzKH8V81aexK6B4AAA==
                 """,
                 """
                 androidx/compose/runtime/SnapshotStateList.class:
-                H4sIAAAAAAAAAI1QXUsbQRQ9M5tkdY26ftTGj9pXG8Q1UhCsCFYoBLYWmpCX
-                PE2yg45JZmRnInnc3+I/6FOhD7L46I8S70ZftC/OwLn3nDncj3l4/HcH4Cs+
-                M9SFTlKjkknUN6NrY2WUjrVTIxm1tLi2l8a1nHAyVtb5YAw7x+2j+ErciGgo
-                9EX0q3cl++7byf8SQ/hW81FiqBwrrdwJg7fzpVNFBX6AMmYYSu5SWYbd+P0T
-                UZOleGDcUOnop3QiEU6Qxkc3Hu3HCvAZ2ICkiSrYPmVJg7bIs2rAazzIs4CH
-                BHlWy7N6aSbPQnbA9/n38v1thYde4T+gEm1G9RC+mmBv4GjqM5NIhsVYaXk+
-                HvVk2ha9ISnLsemLYUekquAv4mxLXWjhxinlQcuM0778oYqH9d/PO3aUVeQ8
-                1dpQC2W0RQOcPqg4NEfxX4TrxKIpB8r1v5j9QwnHBmFlKm7SBarPBgSYo+hh
-                a+ry8Gkaa9imeEieKnnmu/CaWGhikRBhAUtNLGOlC2axig9dlCzmLNYsPlr4
-                TxVS9+dFAgAA
+                H4sIAAAAAAAA/41QTUsjQRSs7smXY3YdXT/irruKJzcsOyqCoCKoIARmd8GE
+                XHLqZBptk3TLdEc8zm/xH3gSPMjg0R8lvoleVi9e6r2qLt571Y9Pd/cAtrDC
+                UBc6ToyKr8KeGV4YK8NkpJ0ayrCpxYU9M67phJORsq4MxrC219qJzsWlCAdC
+                n4b/uuey53b330sMwVutjAJDaU9p5fYZvLWf7SpKKPsoosJQcGfKMvyKPn4R
+                LZmO+sYNlA7/SCdi4QRpfHjpUT6WQyUHMLA+6VcqZ+vUxRsUJUurPq9xP0t9
+                HhBkaS1L64VKlgZsk6/zw+LDdYkHXu7fpBEtlk8K/jvjd9/R6UcmlgxTkdLy
+                72jYlUlLdAekzESmJwZtkaicv4oTTXWqhRsl1PtNM0p68ljlD4snL0Hbyipy
+                HmhtaIUy2mIDnH7pNUr+aYRfiYVjDhTrt5i4oYbjG2FpLK5iibD6YoCPSaoe
+                vo9dHn6M6yKWqW6Tp0qeTx14DXxuYKqBANPUYqaBL5jtgFnMYb6DgsWkxYJF
+                zaL8DHqS9VZKAgAA
                 """,
                 """
                 androidx/compose/runtime/SnapshotStateMap.class:
-                H4sIAAAAAAAAAI1QTU8bMRB99ibZsKSwQKGBftBjoVIXUE80QqKVkCKWVmqq
-                veTkZC0wSexo7SCO+1v4Bz1V6qFa9ciPqjoOvbTlgCW/N/P8bM/M7a/vPwC8
-                xUuGHaHzwqj8OhmaydRYmRQz7dREJj0tpvbCuJ4TTp6JaQjG0OmcHqaX4kok
-                Y6HPk0+DSzl077J7tKP/JYb4Xy1EjaHRUVq5I4bg1U7WQgNhhDqaDDV3oSzD
-                6/TBRdIfK+nIuLHSyZl0IhdOkMYnVwF1zDyEDGxE0rXy2R5F+T5DUpVLEW/z
-                iDdpx1UZVWW7KndrzaqMGRGL+QHfC97Xf940eFzz1w7opVPaGaOnEf9Vy5uR
-                o/o/mFwyLKdKy4+zyUAWX8RgTMpqaoZinIlC+fyPuNBT51q4WUFx1DOzYihP
-                lD/Y/HzXbaasIuex1oa+UEZb7IPTqPyiOvzkCLcoS+Y5UN/9hoWvFHA8JWzM
-                xRd4Rti6MyDCInGA53NXQKeeN7FNfEieFnke9RF0sdTFMiFiDytdrGKtD2bx
-                GOt91C0WLTYsnli0LcLftbcdUGUCAAA=
+                H4sIAAAAAAAA/41QyU4bQRB91eONwYGBbCZ7LlEgUgZQTsRCSiJFshgSKY7m
+                4lPb04LGdrc13UYc51vyB5yQOKBRjvmoKDWGS5ZDDvWq6vXr2n78vLwC8AbP
+                CZvSZLnV2Vk8stOZdSrO58brqYr7Rs7csfV9L706lLMmiNDtHuwlJ/JUxhNp
+                juLPwxM18m/Tf3D7f1OE6E+uiRqh0dVG+31C8HIzbaOBZog6WoSaP9aO8Cr5
+                7yG5x1oytn6iTXyovMykl8yJ6WnAG1MFrQpAoDHzZ7rKtjnKdghxWayEoiNC
+                0WKLyiIsi05ZbNVaZRERO4rErtgO3te/f2uIqFZ92+VKB2wpVUWj3wZ6Pfa8
+                xAebKcJqoo36NJ8OVf5VDifMrCd2JCepzHWV35BLfX1kpJ/nHId9O89H6qOu
+                Hja+XK+caqdZ+c4Yyy20NQ47EHyvm62q8zE+5Cxe5EB96wJL5xwIPGJsLMgX
+                eMzYvhYgxDL7AE8WqgBPF/4BnrHfY02bNbcGCHpY6WG1hwhrHGK9h9u4MwA5
+                3MW9AeoOyw73HToOGw7NXygOZ4ZqAgAA
                 """,
                 """
                 androidx/compose/runtime/State.class:
-                H4sIAAAAAAAAAH1QTUvDQBB9k7RpjF/xu1YQj9WDUfEgfoEXoVARbBGhp7Vd
-                69p0I91t8Zjf4sEf4UGCR3+UOFFPKu7hzbw3O7Nv5+39+QXALlYIq0J3Bonq
-                PETtpH+fGBkNhtqqvowaVlhZAhGqh839+p0YiSgWuhudX9/Jtj04/i0Rwp9a
-                CQWC35X2UsRDSZivrv/VV6yuN5scZ+q9xMZKR2fSio6wgjWnP3LZLuVQIlCP
-                pQeVsy3OOtuEwyydCpyyE2Rp4IQ5+K5/U87SDc/P0pDWaMfZci5mQ7fi7GXp
-                1etT4fXR8yoFvxAW8xk7hLX6/5tgI9QktoHi6OsrYUOLe3Ob2M/6Zs8Sxhqq
-                q4UdDrgcNJLhoC1PVcxk+eJr1qUy6jqWJ1on3KQSbTx+H0Xkh3hVHnjlKDNz
-                4MP9zlwsf8YlVDge8Y0x7glacGsYr2GCEZM5TNUwjbAFMpjBbAuewZzBvMGC
-                waLJaekDuDg1Tf4BAAA=
+                H4sIAAAAAAAA/31Qy07jQBCsthPHmJeT5RECQhzDHtaAOKx4SXtBihSERCKE
+                lNOQDGGIM0aZScTR38KBj+CALI77USvaYU+AuHR1VU/3VPfff88vAPaxQdgU
+                ujdKVO8h6ibD+8TIaDTWVg1l1LLCyhKIUD9qHzTvxEREsdD96Pz6Tnbt4cln
+                iRB+1EooEPy+tJciHkvCUn37q75ifbvdZiw3B4mNlY7OpBU9YQVrznDisl3K
+                g58HEGjA+oPK2Q5nvV3CUZYuBE7VCbI0cMI8+K5/U83Sn56fpSFt0Z6z41xU
+                Qrfm/M7Sq9enwuuj59UKfiEs5jP2CFvN78/BbqhNuYHi5H2fsKXFvblN7LT+
+                a2AJMy3V18KOR1wOWsl41JWnKmaydvE+61IZdR3LP1on3KQSbTz+H0VMd+N7
+                eeC7Y42ZAx/u/8xFbYpVrDMe84sZ7gk6cBuYbWCugXkscIrFBkKUOyCDCn50
+                4BksGSwbrBismpyW3gBNs/uhAwIAAA==
+                """,
+                """
+                androidx/compose/runtime/StructuralEqualityPolicy.class:
+                H4sIAAAAAAAA/5VVW08bRxT+ZteX9WLAcVJqCE2ThjbmljWkdygtIVQxciiq
+                I6qWp7E9dcasd53dWStRX1B/Sp/70KgPiVqpQu1bflTVM7suIQYikOU5Z845
+                c+Y7t9mX//7xF4APscmwxL1W4MvWE6fpd3t+KJwg8pTsCqeugqipooC7m48j
+                7kr1dMd3ZfNpFozhu1qH97njcq/tfNPoiKZaqZ3tyeO98JGvHkSKK+l7iZ/V
+                ky7WVhgKw9IsUgzOBZ1nkWHIrEpPqjUGszy7m4eFnI00bIaUeiRDhjtvgHxG
+                8ATQFo8j2eeu8BTDrfIpiTghmf2BYabmB22nI1Qj4NILKSDPTxCHznbkurzh
+                CvJ+bd9XrvScTr/rSE+JwOOuU/VUQGdkM8ziEoPFAxHDyuMyxm0UcYWBcYbi
+                yatJ0aATyq9rF22GK+XZY1aJlKxuvgmerzRCsiqdlRcL0wzprgjagmHrXEk5
+                JU2nwV+9YOVn7okfeeSqarfnUrquM/xUvmhrngvtufDncQ3v2biBm1SEXiD6
+                0o+o87LNKAjiBsryXs+VokXq1aYb96sNQzepVd2uP1zf3tjMYw6jORLOM1yq
+                DfrjgVC8xRWnDBndvknjzPRi6QVU9H2SP5F6VyGutcQgDg+mbaNkJP/C4YFt
+                WJoZIWoSHbP++dkoHR4s28VMMVViFaPC7mYt4+9fMkbB3JoupKaMSnrZKmSm
+                Yi1x2YS7P7FlFSzS5u5P6MuW6Vm5cMqZhp0/Xjx6C/63rZOpuL1P+br6beKm
+                6vVlKGlm1l81Kg32ht+iBhyvSU9sR92GCB7qudKT4Te5u8sDqfcD4cywrx0e
+                8K6goXvNab7qeSLYcHkYCtrm6rLtcZoB8mDX/Shoiq+ldjc5cLd7AhgqVL00
+                xUdvJyZ1OYmuUm0yRK8STZOenifafUE7RxdQS+eeY+QZMQbWBsZAB1/Smk8M
+                iI7G9R7DOFnpw/eI6rJn5hde4K1fh07vx6evJxaD05qbwNuxPoMScQa+In7U
+                IFEhdp+sk5gaXLI4QGgW3/lt6Ab3GD5zcMPxUOmJw7uk0W52kKIfde3c/MLi
+                C8wM+0pinUtsjtBaeH+A1sIHMVrN3SLOPLpJA9YxXDaHYijExuWjXCfHgZE/
+                YXz/HLO/Y+FZLEhhnVabzG5Q2T6n4O/G6ExsxHSFMg1USb9Idbi9B7MKp4pK
+                FUtYJhZ3qvSF/WgPLMTH+GQPdojREJ+G+CxELsRYzEyEKIWYJP4//rdNjpUH
+                AAA=
+                """
+    )
+
+    val Effects: TestFile = bytecodeStub(
+        filename = "Effects.kt",
+        filepath = "androidx/compose/runtime",
+        checksum = 0xb63b1aec,
+        """
+            package androidx.compose.runtime
+
+            @Composable
+            fun SideEffect(
+                effect: () -> Unit
+            ) {
+                effect()
+            }
+
+            class DisposableEffectScope {
+                inline fun onDispose(
+                    crossinline onDisposeEffect: () -> Unit
+                ): DisposableEffectResult = object : DisposableEffectResult {
+                    override fun dispose() {
+                        onDisposeEffect()
+                    }
+                }
+            }
+
+            interface DisposableEffectResult {
+                fun dispose()
+            }
+
+            private class DisposableEffectImpl(
+                private val effect: DisposableEffectScope.() -> DisposableEffectResult
+            )
+
+            @Composable
+            @Deprecated("Provide at least one key", level = DeprecationLevel.ERROR)
+            fun DisposableEffect(
+                effect: DisposableEffectScope.() -> DisposableEffectResult
+            ): Unit = error("Provide at least one key.")
+
+            @Composable
+            fun DisposableEffect(
+                key1: Any?,
+                effect: DisposableEffectScope.() -> DisposableEffectResult
+            ) {
+                remember(key1) { DisposableEffectImpl(effect) }
+            }
+
+            @Composable
+            fun DisposableEffect(
+                key1: Any?,
+                key2: Any?,
+                effect: DisposableEffectScope.() -> DisposableEffectResult
+            ) {
+                remember(key1, key2) { DisposableEffectImpl(effect) }
+            }
+
+            @Composable
+            fun DisposableEffect(
+                key1: Any?,
+                key2: Any?,
+                key3: Any?,
+                effect: DisposableEffectScope.() -> DisposableEffectResult
+            ) {
+                remember(key1, key2, key3) { DisposableEffectImpl(effect) }
+            }
+
+            @Composable
+            fun DisposableEffect(
+                vararg keys: Any?,
+                effect: DisposableEffectScope.() -> DisposableEffectResult
+            ) {
+                remember(*keys) { DisposableEffectImpl(effect) }
+            }
+
+            internal class LaunchedEffectImpl(
+                private val task: suspend () -> Unit
+            )
+
+            @Deprecated("Provide at least one key", level = DeprecationLevel.ERROR)
+            @Composable
+            fun LaunchedEffect(
+                block: suspend () -> Unit
+            ): Unit = error("Provide at least one key")
+
+            @Composable
+            fun LaunchedEffect(
+                key1: Any?,
+                block: suspend () -> Unit
+            ) {
+                remember(key1) { LaunchedEffectImpl(block) }
+            }
+
+            @Composable
+            fun LaunchedEffect(
+                key1: Any?,
+                key2: Any?,
+                block: suspend () -> Unit
+            ) {
+                remember(key1, key2) { LaunchedEffectImpl(block) }
+            }
+
+            @Composable
+            fun LaunchedEffect(
+                key1: Any?,
+                key2: Any?,
+                key3: Any?,
+                block: suspend () -> Unit
+            ) {
+                remember(key1, key2, key3) { LaunchedEffectImpl(block) }
+            }
+
+            @Composable
+            fun LaunchedEffect(
+                vararg keys: Any?,
+                block: suspend () -> Unit
+            ) {
+                remember(*keys) { LaunchedEffectImpl(block) }
+            }
+        """,
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgMuSSSMxLKcrPTKnQS87PLcgvTtUr
+                Ks0rycxNFeJ0TUtLTS4p9i4R4gpKzU3NTUot8i7h4uNiKUktLhFiCwGS3iVK
+                DFoMAHVSFrpbAAAA
+                """,
+                """
+                androidx/compose/runtime/DisposableEffectImpl.class:
+                H4sIAAAAAAAA/51TS08UQRD+enbZx4iyLPJGQEFZQJgFvS0hUYRkkxUNS4gJ
+                p2a2gV5me8h0L8Eb0Yu/w3/gwWg8GMLRH2Ws3ocgxgAmM9VV1f1VfV1V/ePn
+                t+8AnuIJwxxXlSiUlWPPD2uHoRZeVFdG1oT3Qmqy+U4gVnd3hW+KtcMgCcaQ
+                qfIj7gVc7Xmvdqq0k0SMIbEklTTLDBO50kFoAqm86lHN260r38hQaW+tpS0U
+                prcYPlx1ammudG1qZT88FIXZ6wM2hK4HprDcoDJRCqM9ryrMTsQlUeBKhYY3
+                6ayHZr0eBAW6n2ggU0gzjF6gLpURkeKBV1QmIrj0dRK3GHr9feEftPCvecRr
+                gg4yTOVKl+tXuOAp2yB7xKsTt3HHRSe6GGI5a3eg20UcWYbxqyrciTTupuGg
+                lyFu9qVm8K5fHdtpuvH7qzp00wb9R38YutssXgrDK9xw8jm1oxgNMLMiZQUY
+                2AH5j6W18qRVFhii05MR1xlwXCdzeuLS19Av/KcnKWfg9GTRybPno9n+jDPU
+                k41nnXy8ITvysbOPCSeVsDKT3Bj71/6bs3dxq1E8m3mRWT7ZNu/zrjDkb1oy
+                hoUbV43ms5179dgIGslQtUlsvm0EdZsAPX9gGNJluae4qUeCYXijGbuojqSW
+                FPnZ+WOgUVoJK3SoqySVWK/XdkS0abPby4Y+D7Z4JK3dck5ejvX7FfwR1C2H
+                9cgXa9JiBluYrb+yI0/jHG+0Omunm6xZshwM4jGtCfKnmoNADyWBGObIKtG+
+                Q2tmNut+RWbmC3pmZj+j71MDOU/yDp1MUAwXQ+ii1SNfXxODfgzY2SLN5mOt
+                fEliAiRZK6GDhYacwSKtK+QdIgLD24gVMVLEvSJGMUYqxou4jwfbYBoTmNxG
+                SmNA46FGWuORxpRGTmNaI/ELZm5MUZ8FAAA=
+                """,
+                """
+                androidx/compose/runtime/DisposableEffectResult.class:
+                H4sIAAAAAAAA/5VPzU4CMRicrwvsuiou/qIPQPTiAjHx4MlEjWswJphw4VTY
+                YgrL1tBCOPJcHgxnH8r4LTyBSTOd+X4605/fr28ANzgjxDJPZ0any3hopp/G
+                qng2z52eqvhBW9ZykKnH0UgNXVfZeeZ8ECEay4WMM5l/xG+DMfd8eAQ/3Wwo
+                gnd51SPUOhPjMp3Hr8rJVDp5RxDThcfWVEBQAAg04fpSF6rJLG0RGutVNRR1
+                EYpovQr5iEgEo/p61RZNegkicSGa3nOjmG4TWp1/foKDsG+4LdnriWPxbuaz
+                oXrSGec/727Xe9pqXr3Pc+Ok0ya3FbZECZvgJUIZFWYCJxs8xinft/y0z52g
+                Dy/BToIwwS72mGI/QRUHfZBFhFofJYtDiyOLMuMfURqWxJYBAAA=
+                """,
+                """
+                androidx/compose/runtime/DisposableEffectScope$onDispose$1.class:
+                H4sIAAAAAAAA/8VUXVPTQBQ9mxZaQoHwIQIqVkFtA5Km4hcwzDBYxmpRh2p9
+                4CltQ1mabpgk7fDk9CfpjI6jD06f/VGON0mRjjry8eJD9t69e/bs3bP35vuP
+                r98ALGOVYcUQVcfm1SOtYjcObdfUnKbweMPUnnCX5kbZMnN7e2bFK1bsQ3Pe
+                FmHcnNdjYAzKgdEyNMsQNe1l+YBgMUQYtDOz7phu06JNfQz9a1xwb51hLlWo
+                257FhXbQamh7TVHxuC1cbavrZVbTJYbsaai14/U3RLu6HmxSTi4Qns+QPO2w
+                BGQMDkBCgiGSSpcSiGFYRhQjDFFvn7sMa4WLy0iPEKuGE4bZf+cSwwTpxEXL
+                rhN4IpUu/C4/ZTuJy4O4hCl6hnMqxDB6HNk2PaNqeAbFpEYrQuXC/CHuD2Bg
+                dYofcX/2nryqzpDqtEfkTluWpqTAKJLKOu24PNVpZ6UMexZXpBkpE3k66eOz
+                DPrZNQuLhFKhk5fOp3QMKYaBX3IzFE8vrnMnloCKBQY5DLpLdSortfcYLjzT
+                EYalFe2mUzGfmOVmLXfkmcKlE+lefS3DalJy74rbG696aOTnAYesFpPH3pa8
+                kNSTPZCLV56sFmR9Tl/U9eUVmuRkUqrIa8Lwmg4lE920q2RGClyYL5qNsum8
+                9skYxgp2xbBKhsP9eTeYyAthOpuW4bomdcRITlQs2+WiRpW0b1dJnfDuW9xH
+                T/xNCIbpnTD3Enc5sW4IYXtG8D4MV7predH6YxU6tWeUqqMfTFH8fiVfp+KU
+                MEMfdRjiZLMUWScrkZXVhU8YUj9D+RDg7tFIu9GHYfozgrooQGEUY37Jk9fL
+                OkDeOCFZwPnY7wiyg+pHDH3BNMPbE1I5IFICKp84EUK7xP24H2BYgAKm8YDG
+                KNJYxMOA4y4ekf3PlUFXBOVDr0ACXd1FJI9reczmcR1JcnEjj5uY2wVzMY9b
+                u4i6vnvbxbiLO1ihzb5WS/RpASjzE1u+Yp2EBgAA
+                """,
+                """
+                androidx/compose/runtime/DisposableEffectScope.class:
+                H4sIAAAAAAAA/51UW2/TSBT+xrnYNaVJw603CgsBWgq1E9gLpCBBF7RBoSAC
+                lVCfJs60TOuMkcep+ljxsP9hX/cX7D4VLdIqKm/8KMQZJy3d7kMpljzn/p05
+                c87Mp8///AvgNu4wzHPVjiPZ3vKCqPM20sKLuyqRHeH9KjXJvBWKR6urIkia
+                QfRW2GAMxXW+yb2QqzXvWWudTDYyDPkFqWRynyEzM7s8jBzyLrKwGbLJG6kZ
+                /MbxUtUYhiLVNwmG5kxjI0pCqbz1zY632lVBIiOlvccDzq/NfnuCF0J3w4Qy
+                tI5CXdizv6Liave/K8nlRhSveesiacVcEjhXKkp4P9FSlCx1w5C8CvvF9sMd
+                FBimD+xOqkTEiodeXSUx4chA2xhlOBO8EcHGAOg5j3lHkCPDtZnG4UbVDmia
+                BmStZnp1CqddlHCG4e7xelTe33O5YuMclXp0l9LZGHcxhgkG75inaWOKYaQs
+                y6vlA7PB6gwXj0rMMLrn8lQkvM0TTjqrs5mhu8DM4pgFBLdB+i1pJJ+4doVh
+                pbc94VpjlmsVe9uu5VipYNhUZ431tquWzx7mdv/Mk/hkqpiZsPxsdcTJFnMT
+                Tilbsnzbz/+2+7vz8T3rbe++s2w35+z+UfWZSVFlJnHlO4artFfUwUrdvpOe
+                30jo/i1GbTqkQkMqsdTttET80uCY0Cjg4TKPpZEHyqGmXFM86cbET77oZ6+r
+                TaklmR98nVyG8mHr/uz9x224rpSIF0OutSDRbUbdOBCPpUk2PoBY/h88KrDo
+                +TCfRSdDrwmtPkmeaRDR3PUdOH+n5gqt+VR5AlVah/sOGIJLdJS0wwRlgpeR
+                gWnr6blS8T3OZu59wNjruR1M9nD+r30sl6iDEboWpRTvIsU4hDGNC2Sh6AGy
+                4QpkZbiVxp6kJ7W/kxGiP9Jvs4GQwU8pMKOxN984fk5DPPxCdJH0P9CGL60g
+                U8flOsp1XMFVYnGtjhnMroBpXMfcChwNV+OGRl7jhMZNjYLGPGm+AB149ZDV
+                BQAA
+                """,
+                """
+                androidx/compose/runtime/EffectsKt.class:
+                H4sIAAAAAAAA/+1Y21Mb1xn/Vhe0LALL4q6kjmJIA8JYWnExWBjH5hKrljGV
+                bKhL63QRC16QdhXtSgYnbdxmOtOX/APpQ2f63Je8JG4z43qat/5Rnf7O0UpI
+                aAHBOEwfyoz2fHvOd/l9t3P28O///OM1EU3S7wW6quhbRUPb2o9mjXzBMNVo
+                saRbWl6NLm1vq1nLvG/5SBAosKuUlWhO0XeiDzd3seAjt0BSRttSK4wCDY2k
+                9gwrp+nR3XI+ul3Ss5Zm6GZ02aZiidE1geKncc1V1x/rmpWY50IfpI6FucDf
+                lc2cmgCElFHcie6q1mZR0aBU0XXDUioGVgxrpZTLgatN5YhFkgS6UgdG0y21
+                qCu5aFK3ihDXsqaP/AL1Zp+p2T1bflUpKnkVjAJ9OJI6GpVE3UyGKdkBfj91
+                0SWJOinQaM/BeR8FgU/Ty8aeKlDPyGizBT/1UG8HdVOfQOHTIo7ELWqmHaBW
+                EyXzmP/pNK658eOzctRoJmsU1MRY6wJp1SzlqunvriJZVAtFNatY6hY88+VV
+                01R2EKeB1aJRRiWGFSucUxXTChu6Gt5TDwTy5tSymhNo8KgKeJBiS1DkXUqn
+                H6YFeu8w1slcTt1RchkUj7q0n1ULjN9HV6HoOFvXRRoWSLSMSt6PZs+uBj/9
+                lD6UyEMjyPOchhKfR4GNOJdNhMYkGqJrpyZahhdTTtXYQp6/PrvcxWV++KSG
+                RjfafT/esvZkvpDzUZzFNiTRBE0KNDasDSvD40U1r+Y31eJ4beMbPio8LAsk
+                JAXqhMT2cFVAIA/Sj6VgcyAF+vjkTeIMifrb29B0cak7Q1jjlRBieNKaj+eK
+                3+sfR/f/ZEQnKhHFMD2ycb5w/fkcghcXC+adKdJS9cukZGm56J1iUTnAmf0x
+                9lZoP3i4LdCokxvJUYdJPyXpZxLdo/tnCjV2kHZNL5Qsc1gr46xy0CxQV0pB
+                jJ6pW9UzWGvhdLUZskbRgHu6auJjB6HRS3wDPGSwP5TGmg1XYuXdzBnZPZHS
+                Il0R6LNz7fdvC8vYselujFBln16TaJ1t0aPHpqNRim3QX72lbfJtedwydmx/
+                f/nRtqgL9wZbz+fn20EuHCoK7HJV6QPVUrYUS0HPuvJlN65JAnuI7EE4/fcY
+                4cLivsaoGKgtWXD98ObltPTmpeQKuPgw4Gr48RX2EN0gLldGtiZ62Rh6H4sh
+                V0yI9wdcoe6gJ+iKefjTG3P/669tLrHtni80VmUSA76QZ0CIiSeyTzWzi4H2
+                FgRvnyQoBqQWVMxXVfQFOkL+oCgKQS4U8189VZiNgc5QKtDVAoh3A5dCA9Au
+                BUWuR4gFgm22Lfc93w+vhDcvucLLobsnKGxZTTA006SmZeHu0LItfFxYWlbV
+                Gbpuq2pZpCfUF+jlIn4Ev8rch4D/weVBJQ6yUsYeROxD1u6G+vtF7KxHu0Dy
+                mQ93XJCrtpf2LRV3cEOvgnh0wJWGT9kgEg742UVYsvv/+h6O30j9NlS792eM
+                UjGrLqqbpZ2adXY/LCu5kioIZubBndU6NdJ9rkOKZMJValkaC8vhOpbT/70C
+                iXg4bW9SJ4pUmSATSUnykHxNlidvypI8MRS/KctTUjzGiWkpPsOJG9LEJCdm
+                qjyzNk88ZvPEZZsnHj/0g0fgfM4A2GRsSOZwJmc5MS1NVYgb0vQNTsxIM1Oc
+                mJVmJxkBOHKsQsEdOc4pAFqS8KW3YGzhjn8phUyvlJj/j1jZsCwbWSW3phQ1
+                9m5Ptme0HV2xSkXQ76Qr8JJ6WTM1LN85vD3ianl0tfafnQY26fB/Drj82zJr
+                DvqkSvUsawxGj1MpkUwu3PzZXwcNkpfa8PYcb3OYR+NRVyTY8YouR/5O/QKt
+                f8NOGtrHU8LYRiJ1Ujsd4N1f4cb8AMYXnM9Hn2Fsw4qI8XP8fDiuIEAwNUgh
+                LDFTt8jNhbsrpr6noSfBD76l0e9o/J81e0yLRO/X2erG+/WarXfot9yL3+EX
+                xPq7mPuJSVfoPZXCkDsRSdR2Og8kLBi9YxyJ+5Z7/nuaeDL2HU3V+97H4zQC
+                XSYwjMCeST00yrFFoEDCyjTdgDIRUZ0B5WJKaZZucoy9HLlgI481YetkoZf5
+                4V5FmLAR7kOpl0G4VkE47572MIjXHCHKUGkBgoxgWTAb5xAnsdaBlVsA5sHo
+                5xDdXKoKsY/mQQmcYmBdNti5JrDd7hrYRsi3bchfQnUbxv7xCuRpj3vayzCP
+                O2JOQEEJmBIIaQlzcxzzPFb8sHkHSL3gqGBmueqvYe6vYe6nu6BcnGLo3Tb6
+                j5rQD3qOoG/0YcH24Y922IciwWX4UCmPSOT1K0o9qOXAqUz8UNFLZTTFAsqw
+                TFdpsZaDAaB4wL3oreWgGy20Qg859iFaAkWcqi+YReeCaa9DvurUXJlqcz1y
+                bq6VhuZK1zXXz8/fXKtOzZWpNdc6i9ovmqvgMXQ9B6bHsPcclbDW0FxPTmyu
+                dF2sHp/eXKtOzZU5bK511lwOEJ9C5T4gPEWw9mH2k4bm2mixudJ1zfXLVptr
+                1am5MnXNtc6aywHzM75b+zH2YOwjraG5fn3m5krXNdevztZcq8c3V6apuZzL
+                xE8FxP0FGqmAMnyB5vq0obk+abG56gvm6WnN5aEvOKNJLzH1/++vi//+4ttQ
+                Aen4DfKsbJA7SZtJyiZpi9QkbdNOEvWtbSA5tEt7G9Rv0qBJOZzVJuVN0k0y
+                TCqYNMsn5026a9ISpxdMWjUpbdKnJt02KWFS1KSQSV6TirwuumDVwq/EtZf/
+                C7XJpgW2HAAA
+                """,
+                """
+                androidx/compose/runtime/LaunchedEffectImpl.class:
+                H4sIAAAAAAAA/5VSW08TURD+znbZbleUsgKWi4iCUKiwhfhWQqJETJOKBpSY
+                8HS6Xcppt2fJXhoeG179F/4DH4zEB9Pgmz/KONuLIKiEZPfM5Xwz852Z+fHz
+                6zcAT7HKkOOy4nuicmzZXuPICxzLj2QoGo5V4pG0D53Ki4MDxw6LjSM3CcaQ
+                rvEmt1wuq9brco1ukkgwaOtCinCDYTZbqnuhK6RVazasA0oRCk8G1lZPWy0s
+                7jGI61Dry32A7fleFArpBNamR8xkxGPEOeAdFS5sFHKly8TIGdeaLXl+1ao5
+                YdnngmpwKb2Qd+tte+F25LoFBjXkQV1HimH6AjMhQ8eX3LWKMvQpWNhBErcY
+                Rqkxdr0X/Yb7vOEQkGEhe5XFBc9unKRKrAZxG3cMDGKIIZGN7QEMG1BhMsxc
+                18BBpDCSgoLRmPahCBiWSzcYI722cl37b9r9vzWfYbiPeuWEvMJDTj6l0UzQ
+                8rH40OMDDKxO/mMRW3nSKrSXH9qtCUPJKIaSbrcM+jp6x6ZfV/R2K9NurSl5
+                9nzenEorExmdmYapm6qp5AfyqqmZaoblWT7x/ZS1W2cfNSWt7Sz+D/j+7ETt
+                g1Wqkzw7UUjq4zGlNRYTNfsPOp/GhYH9o1kEMbrdD1bqIUNqV1QlDyPfYZjc
+                6Y6pKJsiEGXXeXa+nTTdTa9CoKES5dyOGmXHf8sJE/PwbO7ucV/Eds85dznX
+                78X8I6mx60W+7WyJOGa8F7N3pTpWacPUznjMeOHIWiRLwTiWSGrk17vDo93V
+                kECOrBLdKyTTOdM4RXrpC+4u5T5j7FMn8gmddwipYQsGXmKI5DL5xroxuIdM
+                vA+kxfVYr14SKySTrFdQgdU5s8iT3CTvBBGY3EeiiKki7hcxjQekYqaIh3i0
+                DxZgFnP70ANkAjwOkAowH2Cho2sBRn4Bxaa1HQ8FAAA=
                 """
     )
 
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt b/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt
index 9ec00a4..5a39288 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt
@@ -16,12 +16,17 @@
 
 package androidx.compose.lint
 
+import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtLambdaExpression
 import org.jetbrains.kotlin.psi.KtParameter
 import org.jetbrains.kotlin.psi.KtSimpleNameExpression
 import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
 import org.jetbrains.kotlin.psi.psiUtil.isAncestor
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
 import org.jetbrains.uast.ULambdaExpression
+import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
+import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
+import org.jetbrains.uast.sourcePsiElement
 import org.jetbrains.uast.toUElement
 
 /**
@@ -143,3 +148,10 @@
             else -> true
         }
     }
+
+fun KotlinUFunctionCallExpression.resolveCall() =
+    (sourcePsiElement as KtElement).getResolvedCall(
+        context = sourcePsi.project
+            .getService(KotlinUastResolveProviderService::class.java)
+            .getBindingContext(sourcePsi)
+    )
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt b/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt
index 48cb5ef..f51de29 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt
@@ -41,10 +41,20 @@
         val State = Name(PackageName, "State")
         val MutableState = Name(PackageName, "MutableState")
         val MutableStateOf = Name(PackageName, "mutableStateOf")
+        val MutableIntStateOf = Name(PackageName, "mutableIntStateOf")
+        val MutableLongStateOf = Name(PackageName, "mutableLongStateOf")
+        val MutableFloatStateOf = Name(PackageName, "mutableFloatStateOf")
+        val MutableDoubleStateOf = Name(PackageName, "mutableDoubleStateOf")
         val MutableStateListOf = Name(PackageName, "mutableStateListOf")
         val MutableStateMapOf = Name(PackageName, "mutableStateMapOf")
         val ProduceState = Name(PackageName, "produceState")
         val Remember = Name(PackageName, "remember")
+        val DisposableEffect = Name(PackageName, "DisposableEffect")
+        val RememberSaveable = Name(PackageName, "rememberSaveable")
+        val LaunchedEffect = Name(PackageName, "LaunchedEffect")
+        val ReusableContent = Name(PackageName, "ReusableContent")
+        val Key = Name(PackageName, "key")
+        val StructuralEqualityPolicy = Name(PackageName, "structuralEqualityPolicy")
     }
     object Ui {
         val PackageName = Package("androidx.compose.ui")
diff --git a/compose/material/material-icons-core/build.gradle b/compose/material/material-icons-core/build.gradle
index 0276b88..615b415 100644
--- a/compose/material/material-icons-core/build.gradle
+++ b/compose/material/material-icons-core/build.gradle
@@ -15,65 +15,100 @@
  */
 
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
 import androidx.compose.material.icons.generator.tasks.IconGenerationTask
 
+
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    dependencies {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-        api("androidx.compose.ui:ui:1.2.1")
-        implementation("androidx.compose.runtime:runtime:1.2.1")
-        implementation(libs.kotlinStdlib)
-
-        samples(project(":compose:material:material-icons-core:material-icons-core-samples"))
-    }
-}
-
-if (AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(":compose:ui:ui"))
             }
+        }
 
-            androidMain.dependencies {
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
                 implementation(libs.kotlinStdlib)
             }
         }
-    }
-    dependencies {
-        samples(project(":compose:material:material-icons-core:material-icons-core-samples"))
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                    api(project(":compose:ui:ui"))
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
+                }
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
+            }
+        }
+
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                }
+            }
+        }
     }
 }
 
 IconGenerationTask.registerCoreIconProject(
         project,
         android,
-        AndroidXComposePlugin.isMultiplatformEnabled(project)
+        true
 )
 
 androidx {
diff --git a/compose/material/material-icons-core/samples/src/main/AndroidManifest.xml b/compose/material/material-icons-core/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 820b02a..0000000
--- a/compose/material/material-icons-core/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<manifest />
diff --git a/compose/material/material-icons-core/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-core/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-core/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended-filled/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-extended-filled/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended-filled/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended-outlined/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-extended-outlined/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended-outlined/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended-rounded/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-extended-rounded/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended-rounded/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended-sharp/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-extended-sharp/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended-sharp/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended-twotone/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-extended-twotone/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended-twotone/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended/build.gradle b/compose/material/material-icons-extended/build.gradle
index 0903147..b7e2fea 100644
--- a/compose/material/material-icons-extended/build.gradle
+++ b/compose/material/material-icons-extended/build.gradle
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
 import androidx.build.RunApiTasks
 import androidx.compose.material.icons.generator.tasks.IconGenerationTask
-import androidx.compose.material.icons.generator.tasks.ExtendedIconGenerationTask
 
 plugins {
     id("AndroidXPlugin")
@@ -26,7 +25,7 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
 IconGenerationTask.registerExtendedIconMainProject(
         project,
@@ -35,38 +34,62 @@
 
 apply from: "shared-dependencies.gradle"
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make an analogous update in the
-         * corresponding block below
-         */
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.kotlinReflect)
-        androidTestImplementation(libs.truth)
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(project(":compose:material:material-icons-core"))
+                implementation(libs.kotlinStdlibCommon)
+                implementation(project(":compose:runtime:runtime"))
+            }
+        }
 
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:foundation:foundation-layout"))
-        androidTestImplementation(project(":compose:ui:ui"))
-        androidTestImplementation(project(":test:screenshot:screenshot"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation("androidx.activity:activity-compose:1.3.1")
-        androidTestImplementation("androidx.appcompat:appcompat:1.3.0")
-    }
-}
+        commonTest {
+            dependencies {
+            }
+        }
 
-if (AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            androidAndroidTest.dependencies {
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
+                }
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:foundation:foundation"))
                 implementation(project(":compose:foundation:foundation-layout"))
                 implementation(project(":compose:ui:ui"))
@@ -83,6 +106,21 @@
                 implementation(libs.truth)
             }
         }
+
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                }
+            }
+        }
     }
 }
 
@@ -102,43 +140,7 @@
 
 }
 
-if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    def allThemeProjects = [
-            project(":compose:material:material-icons-extended-filled"),
-            project(":compose:material:material-icons-extended-outlined"),
-            project(":compose:material:material-icons-extended-rounded"),
-            project(":compose:material:material-icons-extended-sharp"),
-            project(":compose:material:material-icons-extended-twotone")
-    ]
-
-    for (themeProject in allThemeProjects) {
-        project.dependencies.add("embedThemesDebug", themeProject)
-        project.dependencies.add("embedThemesRelease", themeProject)
-    }
-    // Compiling all of the icons in this project takes a while,
-    // so when possible, we compile each theme in its own project and merge them here.
-    // Hopefully we can revert this when parallel compilation is supported:
-    // https://youtrack.jetbrains.com/issue/KT-46085
-    android {
-        libraryVariants.all { v ->
-            if (v.name.toLowerCase().contains("debug")) {
-                v.registerPostJavacGeneratedBytecode(configurations.embedThemesDebug)
-            } else {
-                v.registerPostJavacGeneratedBytecode(configurations.embedThemesRelease)
-            }
-            // Manually set up source jar generation
-            ExtendedIconGenerationTask.registerSourceJarOnly(project, v)
-        }
-    }
-} else {
-    // We're not sure how to compile these icons in parallel when multiplatform is enabled
-    IconGenerationTask.registerExtendedIconThemeProject(
-            project,
-            android,
-            AndroidXComposePlugin.isMultiplatformEnabled(project)
-    )
-}
-
+IconGenerationTask.registerExtendedIconThemeProject(project, android)
 
 androidx {
     name = "Compose Material Icons Extended"
diff --git a/compose/material/material-icons-extended/generate.gradle b/compose/material/material-icons-extended/generate.gradle
index aed94d9..eb45afa 100644
--- a/compose/material/material-icons-extended/generate.gradle
+++ b/compose/material/material-icons-extended/generate.gradle
@@ -25,17 +25,9 @@
 apply plugin: "com.android.library"
 apply plugin: "AndroidXComposePlugin"
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
 apply from: "${buildscript.sourceFile.parentFile}/shared-dependencies.gradle"
 
-if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    // We're not sure how to merge icons jars when multiplatform is enabled
-    IconGenerationTask.registerExtendedIconThemeProject(
-            project,
-            android,
-            AndroidXComposePlugin.isMultiplatformEnabled(project)
-    )
-}
+IconGenerationTask.registerExtendedIconThemeProject(project, android)
 
 dependencies.attributesSchema {
     attribute(iconExportAttr)
diff --git a/compose/material/material-icons-extended/shared-dependencies.gradle b/compose/material/material-icons-extended/shared-dependencies.gradle
index f2cbca1..2de0ef0 100644
--- a/compose/material/material-icons-extended/shared-dependencies.gradle
+++ b/compose/material/material-icons-extended/shared-dependencies.gradle
@@ -18,36 +18,25 @@
 // by its specific theme projects (each of which compile a specific theme)
 
 import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 
-dependencies {
-    if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make an analogous update in the
-         * corresponding block below
-         */
-       api(project(":compose:material:material-icons-core"))
-       implementation(libs.kotlinStdlibCommon)
-       implementation(project(":compose:runtime:runtime"))
-    }
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 }
 
-if (AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
-                api(project(":compose:material:material-icons-core"))
-                implementation(libs.kotlinStdlibCommon)
-                implementation(project(":compose:runtime:runtime"))
-            }
+kotlin {
+    /*
+     * When updating dependencies, make sure to make an analogous update in the
+     * corresponding block above
+     */
+    sourceSets {
+        commonMain.dependencies {
+            api(project(":compose:material:material-icons-core"))
+            implementation(libs.kotlinStdlibCommon)
+            implementation(project(":compose:runtime:runtime"))
         }
     }
 }
diff --git a/compose/material/material-icons-extended/src/androidAndroidTest/AndroidManifest.xml b/compose/material/material-icons-extended/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-icons-extended/src/androidMain/AndroidManifest.xml b/compose/material/material-icons-extended/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/material/material-icons-extended/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index 31a99bb..999f59f 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,48 +23,15 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        api("androidx.compose.foundation:foundation:1.2.1")
-        api("androidx.compose.runtime:runtime:1.2.1")
-
-        implementation(libs.kotlinStdlibCommon)
-        implementation("androidx.compose.animation:animation:1.2.1")
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(":compose:foundation:foundation"))
                 api(project(":compose:runtime:runtime"))
@@ -73,19 +39,55 @@
                 implementation(project(":compose:animation:animation"))
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
+        commonTest {
+            dependencies {
             }
+        }
 
-            androidAndroidTest.dependencies {
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                    api(project(":compose:foundation:foundation"))
+                    api(project(":compose:runtime:runtime"))
+                    implementation(project(":compose:animation:animation"))
+                    implementation(project(":compose:ui:ui-util"))
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
+                }
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:test-utils"))
 
                 implementation(libs.testRules)
@@ -94,6 +96,29 @@
                 implementation(libs.truth)
             }
         }
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+                implementation(libs.truth)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                }
+            }
+        }
     }
 }
 
@@ -105,7 +130,7 @@
     // Disable strict API mode for MPP builds as it will fail to compile androidAndroidTest
     // sources, as it doesn't understand that they are tests and thinks they should have explicit
     // visibility
-    legacyDisableKotlinStrictApiMode = AndroidXComposePlugin.isMultiplatformEnabled(project)
+    legacyDisableKotlinStrictApiMode = true
 }
 
 android {
diff --git a/compose/material/material-ripple/src/androidAndroidTest/AndroidManifest.xml b/compose/material/material-ripple/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index e865f27..0000000
--- a/compose/material/material-ripple/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material-ripple/src/androidMain/AndroidManifest.xml b/compose/material/material-ripple/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/compose/material/material-ripple/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index feb60c4b..7fd3fdd 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -81,7 +81,7 @@
     method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material.BottomDrawerValue getCurrentValue();
-    method public Float? getOffset();
+    method public float getOffset();
     method public androidx.compose.material.BottomDrawerValue getTargetValue();
     method public boolean isClosed();
     method public boolean isExpanded();
@@ -91,7 +91,7 @@
     property public final boolean isClosed;
     property public final boolean isExpanded;
     property public final boolean isOpen;
-    property public final Float? offset;
+    property public final float offset;
     property public final androidx.compose.material.BottomDrawerValue targetValue;
     field public static final androidx.compose.material.BottomDrawerState.Companion Companion;
   }
@@ -385,7 +385,7 @@
     method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public suspend Object? animateTo(androidx.compose.material.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material.DrawerValue getCurrentValue();
-    method @androidx.compose.material.ExperimentalMaterialApi public Float? getOffset();
+    method @androidx.compose.material.ExperimentalMaterialApi public float getOffset();
     method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.material.DrawerValue getTargetValue();
     method public boolean isAnimationRunning();
     method public boolean isClosed();
@@ -396,7 +396,7 @@
     property public final boolean isAnimationRunning;
     property public final boolean isClosed;
     property public final boolean isOpen;
-    property @androidx.compose.material.ExperimentalMaterialApi public final Float? offset;
+    property @androidx.compose.material.ExperimentalMaterialApi public final float offset;
     property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.DrawerValue targetValue;
     field public static final androidx.compose.material.DrawerState.Companion Companion;
   }
@@ -532,7 +532,7 @@
   }
 
   public final class ModalBottomSheetKt {
-    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ModalBottomSheetState sheetState, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ModalBottomSheetState sheetState, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
     method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHalfExpanded);
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index 82e2838..3019ae0 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
 
 plugins {
@@ -24,70 +24,15 @@
     id("AndroidXPaparazziPlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        api("androidx.compose.animation:animation-core:1.2.1")
-        api(project(":compose:foundation:foundation"))
-        api(project(":compose:material:material-icons-core"))
-        api(project(":compose:material:material-ripple"))
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api("androidx.compose.ui:ui:1.2.1")
-        api(project(":compose:ui:ui-text"))
-
-        implementation(libs.kotlinStdlibCommon)
-        implementation("androidx.compose.animation:animation:1.2.1")
-        implementation("androidx.compose.foundation:foundation-layout:1.2.1")
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-
-        // TODO: remove next 3 dependencies when b/202810604 is fixed
-        implementation("androidx.savedstate:savedstate:1.2.1")
-        implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
-        implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-
-        androidTestImplementation(project(":compose:material:material:material-samples"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":test:screenshot:screenshot"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.mockitoKotlin)
-        androidTestImplementation(libs.testUiautomator)
-
-        lintPublish project(":compose:material:material-lint")
-
-        samples(project(":compose:material:material:material-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(":compose:animation:animation-core"))
                 api(project(":compose:foundation:foundation"))
@@ -101,8 +46,37 @@
                 implementation(project(":compose:foundation:foundation-layout"))
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            androidMain.dependencies {
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                    api(project(":compose:animation:animation-core"))
+                    api(project(":compose:runtime:runtime"))
+                    api(project(":compose:ui:ui"))
+                    api(project(":compose:ui:ui-text"))
+                    implementation(project(":compose:animation:animation"))
+                    implementation(project(":compose:foundation:foundation-layout"))
+                    implementation(project(":compose:ui:ui-util"))
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.annotation:annotation:1.1.0")
 
                 // TODO: remove next 3 dependencies when b/202810604 is fixed
@@ -110,23 +84,27 @@
                 implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
                 implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
             }
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+                }
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
             }
+        }
 
-            androidAndroidTest.dependencies {
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:material:material:material-samples"))
                 implementation(project(":compose:test-utils"))
                 implementation(project(":test:screenshot:screenshot"))
@@ -140,18 +118,39 @@
                 implementation(libs.mockitoKotlin)
                 implementation(libs.testUiautomator)
             }
+        }
 
-            desktopTest.dependencies {
-                implementation(project(":compose:ui:ui-test-junit4"))
-                implementation(libs.truth)
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
                 implementation(libs.junit)
-                implementation(libs.skikoCurrentOs)
+                implementation(libs.truth)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                    implementation(project(":compose:ui:ui-test-junit4"))
+                    implementation(libs.truth)
+                    implementation(libs.junit)
+                    implementation(libs.skikoCurrentOs)
+                }
             }
         }
     }
-    dependencies {
-        samples(project(":compose:material:material:material-samples"))
-    }
+}
+
+dependencies {
+    lintPublish project(":compose:material:material-lint")
 }
 
 androidx {
diff --git a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt
index 2fe5d63..d705315 100644
--- a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt
+++ b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt
@@ -157,15 +157,10 @@
         @JvmStatic
         fun registerExtendedIconThemeProject(
             project: Project,
-            libraryExtension: LibraryExtension,
-            isMpp: Boolean
+            libraryExtension: LibraryExtension
         ) {
-            if (isMpp) {
-                ExtendedIconGenerationTask.register(project, null)
-            } else {
-                libraryExtension.libraryVariants.all { variant ->
-                    ExtendedIconGenerationTask.register(project, variant)
-                }
+            libraryExtension.libraryVariants.all { variant ->
+                ExtendedIconGenerationTask.register(project, variant)
             }
 
             // b/175401659 - disable lint as it takes a long time, and most errors should
diff --git a/compose/material/material/integration-tests/material-catalog/src/main/AndroidManifest.xml b/compose/material/material/integration-tests/material-catalog/src/main/AndroidManifest.xml
deleted file mode 100644
index ccc91d4..0000000
--- a/compose/material/material/integration-tests/material-catalog/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BadgeDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BadgeDemo.kt
index 0330c3c5..e03a6c9 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BadgeDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BadgeDemo.kt
@@ -41,6 +41,7 @@
 import androidx.compose.material.icons.filled.Menu
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -56,7 +57,7 @@
 @Composable
 fun BadgeDemo() {
     Column(Modifier.verticalScroll(rememberScrollState())) {
-        var badgeCount by remember { mutableStateOf(8) }
+        var badgeCount by remember { mutableIntStateOf(8) }
         Spacer(Modifier.requiredHeight(24.dp))
         TopAppBarWithBadge(
             { badgeCount = 0 },
@@ -146,7 +147,7 @@
     onArtistsBadgeClick: () -> Unit,
     artistsBadgeCount: Int
 ) {
-    var selectedItem by remember { mutableStateOf(initialSelectedIndex) }
+    var selectedItem by remember { mutableIntStateOf(initialSelectedIndex) }
     val items = listOf("Songs", "Artists", "Playlists", "Something else")
 
     var showSongsBadge by remember { mutableStateOf(true) }
@@ -222,7 +223,7 @@
     onTab1BadgeClick: () -> Unit,
     tab1BadgeCount: Int
 ) {
-    var state by remember { mutableStateOf(initialSelectedIndex) }
+    var state by remember { mutableIntStateOf(initialSelectedIndex) }
     val titles = listOf("TAB 1", "TAB 2", "TAB 3 WITH LOTS OF TEXT")
     val showTabBadgeList = remember { mutableStateListOf(true, true) }
 
@@ -277,7 +278,7 @@
     onTab1BadgeClick: () -> Unit,
     tab1BadgeCount: Int,
 ) {
-    var state by remember { mutableStateOf(0) }
+    var state by remember { mutableIntStateOf(0) }
     val titlesAndIcons = listOf(
         "TAB" to Icons.Filled.Favorite,
         "TAB & ICON" to Icons.Filled.Favorite,
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
index 9cd8b82..9489001 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
@@ -42,7 +42,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -57,12 +57,12 @@
  * as the user scrolls. This has the effect of going from a 'light' theme to a 'dark' theme.
  */
 class DynamicThemeActivity : ComponentActivity() {
-    private val scrollFraction = mutableStateOf(0f)
+    private val scrollFraction = mutableFloatStateOf(0f)
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {
-            val palette = interpolateTheme(scrollFraction.value)
+            val palette = interpolateTheme(scrollFraction.floatValue)
             val darkenedPrimary = palette.darkenedPrimary
             window.statusBarColor = darkenedPrimary
             window.navigationBarColor = darkenedPrimary
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ProgressIndicatorDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ProgressIndicatorDemo.kt
index f1f3651..b3aaf5c 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ProgressIndicatorDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ProgressIndicatorDemo.kt
@@ -36,7 +36,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -92,7 +92,7 @@
 }
 
 private class ProgressState {
-    var progress by mutableStateOf(0f)
+    var progress by mutableFloatStateOf(0f)
 
     fun start() {
         handler.postDelayed(updateProgress, 500)
diff --git a/compose/material/material/samples/src/main/AndroidManifest.xml b/compose/material/material/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index dc2f180..0000000
--- a/compose/material/material/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/material/material/src/androidAndroidTest/AndroidManifest.xml b/compose/material/material/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/compose/material/material/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt
index 7af8d54..64a43ff 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt
@@ -199,24 +199,24 @@
             .getUnclippedBoundsInRoot()
         val textBounds = rule.onNodeWithText("ItemText").getUnclippedBoundsInRoot()
 
-        // Distance from the bottom to the text baseline and from the text baseline to the
-        // bottom of the icon
+        val topPadding = 8.dp
+        // Distance from the text baseline to the bottom of the icon
         val textBaseline = 12.dp
 
         // Relative position of the baseline to the top of text
-        val relativeTextBaseline = rule.onNodeWithText("ItemText").getLastBaselinePosition()
+        val relativeTextBaseline = rule.onNodeWithText("ItemText").getFirstBaselinePosition()
         // Absolute y position of the text baseline
         val absoluteTextBaseline = textBounds.top + relativeTextBaseline
 
-        val itemBottom = itemBounds.height + itemBounds.top
-        // Text baseline should be 12.dp from the bottom of the item
-        absoluteTextBaseline.assertIsEqualTo(itemBottom - textBaseline)
+        val iconBottom = iconBounds.height + iconBounds.top
+        // Text baseline should be 12.dp from the bottom of the icon
+        absoluteTextBaseline.assertIsEqualTo(iconBottom + textBaseline)
 
         rule.onNodeWithTag("icon", useUnmergedTree = true)
-            // The icon should be centered in the item
+            // The icon should be horizontally centered in the item
             .assertLeftPositionInRootIsEqualTo((itemBounds.width - iconBounds.width) / 2)
-            // The bottom of the icon is 12.dp above the text baseline
-            .assertTopPositionInRootIsEqualTo(absoluteTextBaseline - 12.dp - iconBounds.height)
+            // The icon is 8.dp from the top
+            .assertTopPositionInRootIsEqualTo(topPadding)
     }
 
     @Test
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
index 9043b15..d5d6e55 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -36,6 +36,10 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.platform.testTag
@@ -56,11 +60,13 @@
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeDown
 import androidx.compose.ui.test.swipeUp
+import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.coerceAtMost
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
@@ -875,6 +881,7 @@
                         }
                     }
                 },
+                sheetGesturesEnabled = true,
                 content = { Box(Modifier.fillMaxSize()) }
             )
         }
@@ -926,6 +933,53 @@
     }
 
     @Test
+    fun modalBottomSheet_gesturesDisabled_doesNotParticipateInNestedScroll() =
+        runBlocking(AutoTestFrameClock()) {
+            lateinit var sheetState: ModalBottomSheetState
+            val sheetContentTag = "sheetContent"
+            val scrollConnection = object : NestedScrollConnection {}
+            val scrollDispatcher = NestedScrollDispatcher()
+            val sheetHeight = 300.dp
+            val sheetHeightPx = with(rule.density) { sheetHeight.toPx() }
+
+            rule.setContent {
+                sheetState = rememberModalBottomSheetState(
+                    initialValue = ModalBottomSheetValue.Expanded,
+                )
+                ModalBottomSheetLayout(
+                    sheetState = sheetState,
+                    sheetContent = {
+                        Box(
+                            Modifier
+                                .fillMaxWidth()
+                                .requiredHeight(sheetHeight)
+                                .nestedScroll(scrollConnection, scrollDispatcher)
+                                .testTag(sheetContentTag),
+                        )
+                    },
+                    sheetGesturesEnabled = false,
+                    content = { Box(Modifier.fillMaxSize()) },
+                )
+            }
+
+            assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Expanded)
+
+            val offsetBeforeScroll = sheetState.requireOffset()
+            scrollDispatcher.dispatchPreScroll(
+                Offset(x = 0f, y = -sheetHeightPx),
+                NestedScrollSource.Drag,
+            )
+            rule.waitForIdle()
+            assertWithMessage("Offset after scroll is equal to offset before scroll")
+                .that(sheetState.requireOffset()).isEqualTo(offsetBeforeScroll)
+
+            val highFlingVelocity = Velocity(x = 0f, y = with(rule.density) { 500.dp.toPx() })
+            scrollDispatcher.dispatchPreFling(highFlingVelocity)
+            rule.waitForIdle()
+            assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Expanded)
+        }
+
+    @Test
     fun modalBottomSheet_anchorsChange_retainsCurrentValue() {
         lateinit var state: ModalBottomSheetState
         var amountOfItems by mutableStateOf(0)
@@ -1266,7 +1320,7 @@
             ModalBottomSheetState(ModalBottomSheetValue.HalfExpanded, density = rule.density)
 
         assertThat(sheetState.anchoredDraggableState.anchors).isEmpty()
-        assertThat(sheetState.anchoredDraggableState.offset).isNull()
+        assertThat(sheetState.anchoredDraggableState.offset).isNaN()
 
         stateRestorationTester.emulateSavedInstanceStateRestore()
         rule.waitForIdle()
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
index b791476..8425818 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
@@ -425,6 +425,28 @@
     }
 
     @Test
+    fun testOutlinedTextField_labelHeight_contributesToTextFieldMeasurements_whenUnfocused() {
+        val tfSize = Ref<IntSize>()
+        val labelHeight = 200.dp
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                modifier = Modifier.testTag(TextfieldTag).onGloballyPositioned {
+                    tfSize.value = it.size
+                },
+                label = {
+                    Box(Modifier.size(width = 50.dp, height = labelHeight))
+                },
+            )
+        }
+
+        rule.runOnIdleWithDensity {
+            assertThat(tfSize.value!!.height).isAtLeast(labelHeight.roundToPx())
+        }
+    }
+
+    @Test
     fun testOutlinedTextField_labelWidth_isNotAffectedByTrailingIcon_whenFocused() {
         val textFieldWidth = 100.dp
         val labelRequestedWidth = 65.dp
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
index f3fecc2..777adeb 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
@@ -38,6 +38,8 @@
 import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -523,6 +525,7 @@
     ) {
         var size: IntSize? = null
         var position: Offset? = null
+        val focusRequester = FocusRequester()
         rule.setMaterialContent {
             CompositionLocalProvider(
                 LocalLayoutDirection provides layoutDirection,
@@ -534,6 +537,7 @@
                     BasicTextField(
                         value = value,
                         onValueChange = {},
+                        modifier = Modifier.focusRequester(focusRequester),
                         singleLine = singleLine,
                         interactionSource = interactionSource
                     ) {
@@ -560,6 +564,10 @@
             }
         }
 
+        rule.runOnUiThread {
+            focusRequester.requestFocus()
+        }
+
         rule.runOnIdle {
             with(Density) {
                 assertThat(size).isNotNull()
@@ -624,6 +632,7 @@
     ) {
         var size: IntSize? = null
         var position: Offset? = null
+        val focusRequester = FocusRequester()
         rule.setMaterialContent {
             CompositionLocalProvider(
                 LocalLayoutDirection provides layoutDirection,
@@ -635,6 +644,7 @@
                     BasicTextField(
                         value = value,
                         onValueChange = {},
+                        modifier = Modifier.focusRequester(focusRequester),
                         singleLine = singleLine,
                         interactionSource = interactionSource
                     ) {
@@ -664,6 +674,10 @@
             }
         }
 
+        rule.runOnUiThread {
+            focusRequester.requestFocus()
+        }
+
         rule.runOnIdle {
             with(Density) {
                 assertThat(size).isNotNull()
diff --git a/compose/material/material/src/androidMain/AndroidManifest.xml b/compose/material/material/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/compose/material/material/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
index 660f1c2..7cba51b 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
@@ -41,6 +41,7 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -95,8 +96,8 @@
 ) {
     val density = LocalDensity.current
     val view = LocalView.current
-    var width by remember { mutableStateOf(0) }
-    var menuHeight by remember { mutableStateOf(0) }
+    var width by remember { mutableIntStateOf(0) }
+    var menuHeight by remember { mutableIntStateOf(0) }
     val verticalMarginInPx = with(density) { MenuVerticalMargin.roundToPx() }
     val coordinates = remember { Ref<LayoutCoordinates>() }
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
index 6e204b8..c93036a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
@@ -31,6 +31,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -168,37 +169,38 @@
     val targetValue: T by derivedStateOf {
         animationTarget ?: run {
             val currentOffset = offset
-            if (currentOffset != null) {
+            if (!currentOffset.isNaN()) {
                 computeTarget(currentOffset, currentValue, velocity = 0f)
             } else currentValue
         }
     }
 
     /**
-     * The current offset, or null if it has not been initialized yet.
+     * The current offset, or [Float.NaN] if it has not been initialized yet.
      *
-     * The offset will be initialized during the first measurement phase of the node that the
-     * [anchoredDraggable] modifier is attached to. These are the phases:
-     * Composition { -> Effects } -> Layout { Measurement -> Placement } -> Drawing
-     * During the first composition, the offset will be null. In subsequent compositions, the offset
-     * will be derived from the anchors of the previous pass.
-     * Always prefer accessing the offset from a LaunchedEffect as it will be scheduled to be
-     * executed the next frame, after layout.
+     * The offset will be initialized when the anchors are first set through [updateAnchors].
      *
-     * To guarantee stricter semantics, consider using [requireOffset].
+     * Strongly consider using [requireOffset] which will throw if the offset is read before it is
+     * initialized. This helps catch issues early in your workflow.
      */
-    @get:Suppress("AutoBoxing")
-    var offset: Float? by mutableStateOf(null)
+    // Todo: Use primitive state when b/281205384 is fixed
+    @Suppress("AutoboxingStateCreation")
+    var offset: Float by mutableStateOf(Float.NaN)
         private set
 
     /**
      * Require the current offset.
      *
+     * @see offset
+     *
      * @throws IllegalStateException If the offset has not been initialized yet
      */
-    fun requireOffset(): Float = checkNotNull(offset) {
-        "The offset was read before being initialized. Did you access the offset in a phase " +
-            "before layout, like effects or composition?"
+    fun requireOffset(): Float {
+        check(!offset.isNaN()) {
+            "The offset was read before being initialized. Did you access the offset in a phase " +
+                "before layout, like effects or composition?"
+        }
+        return offset
     }
 
     /**
@@ -228,7 +230,7 @@
      * You can use this value to provide smooth reconciliation behavior when re-targeting an
      * animation.
      */
-    var lastVelocity: Float by mutableStateOf(0f)
+    var lastVelocity: Float by mutableFloatStateOf(0f)
         private set
 
     /**
@@ -412,12 +414,12 @@
                 }
             } finally {
                 if (targetValue != null) animationTarget = null
-                val endState = offset?.let { endOffset ->
+                val endState =
                     anchors
                         .entries
-                        .firstOrNull { (_, anchorOffset) -> abs(anchorOffset - endOffset) < 0.5f }
+                        .firstOrNull { (_, anchorOffset) -> abs(anchorOffset - offset) < 0.5f }
                         ?.key
-                }
+
                 if (endState != null && confirmValueChange.invoke(endState)) {
                     currentValue = endState
                 }
@@ -428,7 +430,7 @@
     }
 
     internal fun newOffsetForDelta(delta: Float) =
-        ((offset ?: 0f) + delta).coerceIn(minOffset, maxOffset)
+        ((if (offset.isNaN()) 0f else offset) + delta).coerceIn(minOffset, maxOffset)
 
     /**
      * Drag by the [delta], coerce it in the bounds and dispatch it to the [AnchoredDraggableState].
@@ -437,7 +439,7 @@
      */
     fun dispatchRawDelta(delta: Float): Float {
         val newOffset = newOffsetForDelta(delta)
-        val oldOffset = offset ?: 0f
+        val oldOffset = if (offset.isNaN()) 0f else offset
         offset = newOffset
         return newOffset - oldOffset
     }
@@ -550,7 +552,7 @@
     anchoredDrag(targetValue = targetValue) { anchors ->
         val targetOffset = anchors[targetValue]
         if (targetOffset != null) {
-            var prev = offset ?: 0f
+            var prev = if (offset.isNaN()) 0f else offset
             animate(prev, targetOffset, velocity, animationSpec) { value, velocity ->
                 // Our onDrag coerces the value within the bounds, but an animation may
                 // overshoot, for example a spring animation or an overshooting interpolator
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
index 60b56ac..cad60b6 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
@@ -27,8 +27,8 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.material.ripple.rememberRipple
@@ -41,7 +41,7 @@
 import androidx.compose.ui.draw.alpha
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.lerp
-import androidx.compose.ui.layout.LastBaseline
+import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
@@ -337,20 +337,19 @@
 ): MeasureResult {
     val height = constraints.maxHeight
 
-    // TODO: consider multiple lines of text here, not really supported by spec but we should
-    // have a better strategy than overlapping the icon and label
-    val baseline = labelPlaceable[LastBaseline]
-
+    val firstBaseline = labelPlaceable[FirstBaseline]
     val baselineOffset = CombinedItemTextBaseline.roundToPx()
+    val netBaselineAdjustment = baselineOffset - firstBaseline
 
-    // Label should be [baselineOffset] from the bottom
-    val labelY = height - baseline - baselineOffset
+    val contentHeight = iconPlaceable.height + labelPlaceable.height + netBaselineAdjustment
+    val contentVerticalPadding = ((height - contentHeight) / 2).coerceAtLeast(0)
 
     val unselectedIconY = (height - iconPlaceable.height) / 2
+    // Icon should be [contentVerticalPadding] from the top
+    val selectedIconY = contentVerticalPadding
 
-    // Icon should be [baselineOffset] from the text baseline, which is itself
-    // [baselineOffset] from the bottom
-    val selectedIconY = height - (baselineOffset * 2) - iconPlaceable.height
+    // Label's first baseline should be [baselineOffset] below the icon
+    val labelY = selectedIconY + iconPlaceable.height + netBaselineAdjustment
 
     val containerWidth = max(labelPlaceable.width, iconPlaceable.width)
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index c791f90..f42d552 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -221,15 +221,14 @@
         get() = anchoredDraggableState.targetValue
 
     /**
-     * The current position (in pixels) of the drawer sheet, or null before the offset is
+     * The current position (in pixels) of the drawer sheet, or [Float.NaN] before the offset is
      * initialized.
      * @see [AnchoredDraggableState.offset] for more information.
      */
     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:Suppress("AutoBoxing")
     @ExperimentalMaterialApi
     @get:ExperimentalMaterialApi
-    val offset: Float?
+    val offset: Float
         get() = anchoredDraggableState.offset
 
     internal fun requireOffset(): Float = anchoredDraggableState.requireOffset()
@@ -313,11 +312,10 @@
         get() = anchoredDraggableState.targetValue
 
     /**
-     * The current offset, or null if it has not been initialized yet.
+     * The current offset, or [Float.NaN] if it has not been initialized yet.
      **/
     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:Suppress("AutoBoxing")
-    val offset: Float?
+    val offset: Float
         get() = anchoredDraggableState.offset
 
     internal fun requireOffset(): Float = anchoredDraggableState.requireOffset()
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index 9d1da3f..8b9eef6 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -528,6 +528,7 @@
  * @param sheetContent The content of the bottom sheet.
  * @param modifier Optional [Modifier] for the entire component.
  * @param sheetState The state of the bottom sheet.
+ * @param sheetGesturesEnabled Whether the bottom sheet can be interacted with by gestures.
  * @param sheetShape The shape of the bottom sheet.
  * @param sheetElevation The elevation of the bottom sheet.
  * @param sheetBackgroundColor The background color of the bottom sheet.
@@ -547,6 +548,7 @@
     modifier: Modifier = Modifier,
     sheetState: ModalBottomSheetState =
         rememberModalBottomSheetState(Hidden),
+    sheetGesturesEnabled: Boolean = true,
     sheetShape: Shape = MaterialTheme.shapes.large,
     sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
     sheetBackgroundColor: Color = MaterialTheme.colors.surface,
@@ -585,13 +587,17 @@
                 .align(Alignment.TopCenter) // We offset from the top so we'll center from there
                 .widthIn(max = MaxModalBottomSheetWidth)
                 .fillMaxWidth()
-                .nestedScroll(
-                    remember(sheetState.anchoredDraggableState, orientation) {
-                        ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
-                            state = sheetState.anchoredDraggableState,
-                            orientation = orientation
+                .then(
+                    if (sheetGesturesEnabled) {
+                        Modifier.nestedScroll(
+                            remember(sheetState.anchoredDraggableState, orientation) {
+                                ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
+                                    state = sheetState.anchoredDraggableState,
+                                    orientation = orientation
+                                )
+                            }
                         )
-                    }
+                    } else Modifier
                 )
                 .offset {
                     IntOffset(
@@ -604,7 +610,8 @@
                 .anchoredDraggable(
                     state = sheetState.anchoredDraggableState,
                     orientation = orientation,
-                    enabled = sheetState.anchoredDraggableState.currentValue != Hidden,
+                    enabled = sheetGesturesEnabled &&
+                        sheetState.anchoredDraggableState.currentValue != Hidden,
                 )
                 .onSizeChanged { sheetSize ->
                     val anchors = buildMap {
@@ -619,37 +626,45 @@
                     }
                     sheetState.anchoredDraggableState.updateAnchors(anchors, anchorChangeCallback)
                 }
-                .semantics {
-                    if (sheetState.isVisible) {
-                        dismiss {
-                            if (sheetState.anchoredDraggableState.confirmValueChange(Hidden)) {
-                                scope.launch { sheetState.hide() }
-                            }
-                            true
-                        }
-                        if (sheetState.anchoredDraggableState.currentValue == HalfExpanded) {
-                            expand {
-                                if (sheetState.anchoredDraggableState.confirmValueChange(
-                                        Expanded
-                                    )
-                                ) {
-                                    scope.launch { sheetState.expand() }
+                .then(
+                    if (sheetGesturesEnabled) {
+                        Modifier.semantics {
+                            if (sheetState.isVisible) {
+                                dismiss {
+                                    if (
+                                        sheetState.anchoredDraggableState.confirmValueChange(Hidden)
+                                    ) {
+                                        scope.launch { sheetState.hide() }
+                                    }
+                                    true
                                 }
-                                true
-                            }
-                        } else if (sheetState.hasHalfExpandedState) {
-                            collapse {
-                                if (sheetState.anchoredDraggableState.confirmValueChange(
-                                        HalfExpanded
-                                    )
+                                if (sheetState.anchoredDraggableState.currentValue
+                                    == HalfExpanded
                                 ) {
-                                    scope.launch { sheetState.halfExpand() }
+                                    expand {
+                                        if (sheetState.anchoredDraggableState.confirmValueChange(
+                                                Expanded
+                                            )
+                                        ) {
+                                            scope.launch { sheetState.expand() }
+                                        }
+                                        true
+                                    }
+                                } else if (sheetState.hasHalfExpandedState) {
+                                    collapse {
+                                        if (sheetState.anchoredDraggableState.confirmValueChange(
+                                                HalfExpanded
+                                            )
+                                        ) {
+                                            scope.launch { sheetState.halfExpand() }
+                                        }
+                                        true
+                                    }
                                 }
-                                true
                             }
                         }
-                    }
-                },
+                    } else Modifier
+                ),
             shape = sheetShape,
             elevation = sheetElevation,
             color = sheetBackgroundColor,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index 6555ac1..5f3120a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -589,16 +589,15 @@
         )
 
         // measure label
-        val isLabelInMiddleSection = animationProgress < 1f
         val labelHorizontalPaddingOffset =
             paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
                 paddingValues.calculateRightPadding(layoutDirection).roundToPx()
         val labelConstraints = relaxedConstraints.offset(
-            horizontal = if (isLabelInMiddleSection) {
-                -occupiedSpaceHorizontally - labelHorizontalPaddingOffset
-            } else {
-                -labelHorizontalPaddingOffset
-            },
+            horizontal = lerp(
+                -occupiedSpaceHorizontally - labelHorizontalPaddingOffset,
+                -labelHorizontalPaddingOffset,
+                animationProgress,
+            ),
             vertical = -bottomPadding
         )
         val labelPlaceable =
@@ -633,21 +632,22 @@
                 textFieldPlaceableWidth = textFieldPlaceable.width,
                 labelPlaceableWidth = widthOrZero(labelPlaceable),
                 placeholderPlaceableWidth = widthOrZero(placeholderPlaceable),
-                isLabelInMiddleSection = isLabelInMiddleSection,
+                animationProgress = animationProgress,
                 constraints = constraints,
                 density = density,
                 paddingValues = paddingValues,
             )
         val height =
             calculateHeight(
-                heightOrZero(leadingPlaceable),
-                heightOrZero(trailingPlaceable),
-                textFieldPlaceable.height,
-                heightOrZero(labelPlaceable),
-                heightOrZero(placeholderPlaceable),
-                constraints,
-                density,
-                paddingValues
+                leadingPlaceableHeight = heightOrZero(leadingPlaceable),
+                trailingPlaceableHeight = heightOrZero(trailingPlaceable),
+                textFieldPlaceableHeight = textFieldPlaceable.height,
+                labelPlaceableHeight = heightOrZero(labelPlaceable),
+                placeholderPlaceableHeight = heightOrZero(placeholderPlaceable),
+                animationProgress = animationProgress,
+                constraints = constraints,
+                density = density,
+                paddingValues = paddingValues,
             )
 
         val borderPlaceable = measurables.first { it.layoutId == BorderId }.measure(
@@ -738,7 +738,7 @@
             textFieldPlaceableWidth = textFieldWidth,
             labelPlaceableWidth = labelWidth,
             placeholderPlaceableWidth = placeholderWidth,
-            isLabelInMiddleSection = animationProgress < 1f,
+            animationProgress = animationProgress,
             constraints = ZeroConstraints,
             density = density,
             paddingValues = paddingValues,
@@ -770,6 +770,7 @@
             textFieldPlaceableHeight = textFieldHeight,
             labelPlaceableHeight = labelHeight,
             placeholderPlaceableHeight = placeholderHeight,
+            animationProgress = animationProgress,
             constraints = ZeroConstraints,
             density = density,
             paddingValues = paddingValues
@@ -787,27 +788,25 @@
     textFieldPlaceableWidth: Int,
     labelPlaceableWidth: Int,
     placeholderPlaceableWidth: Int,
-    isLabelInMiddleSection: Boolean,
+    animationProgress: Float,
     constraints: Constraints,
     density: Float,
     paddingValues: PaddingValues,
 ): Int {
     val middleSection = maxOf(
         textFieldPlaceableWidth,
-        if (isLabelInMiddleSection) labelPlaceableWidth else 0,
+        lerp(labelPlaceableWidth, 0, animationProgress),
         placeholderPlaceableWidth
     )
     val wrappedWidth =
         leadingPlaceableWidth + middleSection + trailingPlaceableWidth
+
+    // Actual LayoutDirection doesn't matter; we only need the sum
+    val labelHorizontalPadding = (paddingValues.calculateLeftPadding(LayoutDirection.Ltr) +
+        paddingValues.calculateRightPadding(LayoutDirection.Ltr)).value * density
     val focusedLabelWidth =
-        if (!isLabelInMiddleSection) {
-            // Actual LayoutDirection doesn't matter; we only need the sum
-            val labelHorizontalPadding = (paddingValues.calculateLeftPadding(LayoutDirection.Ltr) +
-                paddingValues.calculateRightPadding(LayoutDirection.Ltr)).value * density
-            labelPlaceableWidth + labelHorizontalPadding.roundToInt()
-        } else {
-            0
-        }
+        ((labelPlaceableWidth + labelHorizontalPadding) * animationProgress).roundToInt()
+
     return maxOf(wrappedWidth, focusedLabelWidth, constraints.minWidth)
 }
 
@@ -821,23 +820,25 @@
     textFieldPlaceableHeight: Int,
     labelPlaceableHeight: Int,
     placeholderPlaceableHeight: Int,
+    animationProgress: Float,
     constraints: Constraints,
     density: Float,
     paddingValues: PaddingValues
 ): Int {
-    // middle section is defined as a height of the text field or placeholder ( whichever is
-    // taller) plus 16.dp or half height of the label if it is taller, given that the label
-    // is vertically centered to the top edge of the resulting text field's container
-    val inputFieldHeight = max(
+    val inputFieldHeight = maxOf(
         textFieldPlaceableHeight,
-        placeholderPlaceableHeight
+        placeholderPlaceableHeight,
+        lerp(labelPlaceableHeight, 0, animationProgress),
     )
     val topPadding = paddingValues.calculateTopPadding().value * density
-    val bottomPadding = paddingValues.calculateBottomPadding().value * density
-    val middleSectionHeight = inputFieldHeight + bottomPadding + max(
+    val actualTopPadding = lerp(
         topPadding,
-        labelPlaceableHeight / 2f
+        max(topPadding, labelPlaceableHeight / 2f),
+        animationProgress,
     )
+    val bottomPadding = paddingValues.calculateBottomPadding().value * density
+    val middleSectionHeight = actualTopPadding + inputFieldHeight + bottomPadding
+
     return max(
         constraints.minHeight,
         maxOf(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
index b30abb2..b3361e0 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
@@ -194,7 +194,7 @@
 
     internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f })
 
-    internal var velocityThreshold by mutableStateOf(0f)
+    internal var velocityThreshold by mutableFloatStateOf(0f)
 
     internal var resistance: ResistanceConfig? by mutableStateOf(null)
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshState.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshState.kt
index 96983f1..746c057 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshState.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshState.kt
@@ -24,6 +24,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -121,10 +122,10 @@
     private val adjustedDistancePulled by derivedStateOf { distancePulled * DragMultiplier }
 
     private var _refreshing by mutableStateOf(false)
-    private var _position by mutableStateOf(0f)
-    private var distancePulled by mutableStateOf(0f)
-    private var _threshold by mutableStateOf(threshold)
-    private var _refreshingOffset by mutableStateOf(refreshingOffset)
+    private var _position by mutableFloatStateOf(0f)
+    private var distancePulled by mutableFloatStateOf(0f)
+    private var _threshold by mutableFloatStateOf(threshold)
+    private var _refreshingOffset by mutableFloatStateOf(refreshingOffset)
 
     internal fun onPull(pullDelta: Float): Float {
         if (_refreshing) return 0f // Already refreshing, do nothing.
diff --git a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
index c197e65..ea91b6c 100644
--- a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
+++ b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
@@ -232,6 +232,9 @@
  * @param expanded Whether the menu is currently open and visible to the user
  * @param onDismissRequest Called when the user requests to dismiss the menu, such as by
  * tapping outside the menu's bounds
+ * @param focusable Sets the ability for the menu to capture focus
+ * @param modifier The modifier for this layout.
+ * @param content The content lambda.
  */
 @Deprecated(
     level = DeprecationLevel.HIDDEN,
diff --git a/compose/material3/material3-adaptive/build.gradle b/compose/material3/material3-adaptive/build.gradle
index c263c79..b1aaced 100644
--- a/compose/material3/material3-adaptive/build.gradle
+++ b/compose/material3/material3-adaptive/build.gradle
@@ -17,7 +17,7 @@
 import androidx.build.AndroidXComposePlugin
 import androidx.build.LibraryType
 import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.KmpPlatformsKt
 
 plugins {
     id("AndroidXPlugin")
@@ -25,49 +25,61 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the analogous update in the
-         * corresponding block below
-         */
-        implementation(libs.kotlinStdlibCommon)
-
-        api("androidx.annotation:annotation:1.1.0")
-
-        api(project(":compose:foundation:foundation"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(":compose:foundation:foundation"))
             }
+        }
 
-            androidMain.dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+        commonTest {
+            dependencies {
             }
+        }
 
-            desktopMain.dependencies {
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
                 implementation(libs.kotlinStdlib)
             }
         }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api("androidx.annotation:annotation:1.1.0")
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                }
+            }
+        }
+
+        jvmTest {
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+            }
+        }
+
+        androidTest {
+            dependsOn(jvmTest)
+        }
     }
 }
 
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index 9eb3383..495dd73 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
+import androidx.build.KmpPlatformsKt
 import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -25,69 +25,73 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        implementation(libs.kotlinStdlibCommon)
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api("androidx.compose.ui:ui:1.2.1")
-        api("androidx.compose.ui:ui-unit:1.2.1")
-        implementation("androidx.window:window:1.0.0")
-
-        testImplementation(libs.kotlinTest)
-        testImplementation(libs.truth)
-
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-
-        samples(project(":compose:material3:material3-window-size-class:material3-window-size-class-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    kotlin {
-        android()
-        jvm("desktop")
-
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(":compose:runtime:runtime"))
                 api(project(":compose:ui:ui"))
                 api(project(":compose:ui:ui-unit"))
             }
+        }
 
-            jvmMain.dependencies {
+        commonTest {
+            dependencies {
+
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
                 implementation(libs.kotlinStdlib)
             }
+        }
 
-            androidMain.dependencies {
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                    // Because dependencies are pinned in the android/common code.
+                    api(project(":compose:runtime:runtime"))
+                    api(project(":compose:ui:ui"))
+                    api(project(":compose:ui:ui-unit"))
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 implementation("androidx.window:window:1.0.0")
             }
+        }
 
-            androidMain.dependsOn(jvmMain)
-            desktopMain.dependsOn(jvmMain)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
 
-            androidTest.dependencies {
-                implementation(libs.kotlinTest)
-                implementation(libs.truth)
+                }
             }
+        }
 
-            androidAndroidTest.dependencies {
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:test-utils"))
                 implementation(project(":compose:foundation:foundation"))
                 implementation(libs.testRules)
@@ -96,9 +100,24 @@
                 implementation(libs.truth)
             }
         }
-    }
-    dependencies {
-        samples(project(":compose:material3:material3-window-size-class:material3-window-size-class-samples"))
+
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.kotlinTest)
+                implementation(libs.truth)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+
+                }
+            }
+        }
     }
 }
 
diff --git a/compose/material3/material3-window-size-class/samples/src/main/AndroidManifest.xml b/compose/material3/material3-window-size-class/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index a7cd314..0000000
--- a/compose/material3/material3-window-size-class/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/material3/material3-window-size-class/src/androidAndroidTest/AndroidManifest.xml b/compose/material3/material3-window-size-class/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index a9ad70f..0000000
--- a/compose/material3/material3-window-size-class/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/compose/material3/material3-window-size-class/src/androidMain/AndroidManifest.xml b/compose/material3/material3-window-size-class/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index a9ad70f..0000000
--- a/compose/material3/material3-window-size-class/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/AndroidManifest.xml b/compose/material3/material3/integration-tests/material3-catalog/src/main/AndroidManifest.xml
deleted file mode 100644
index ccc91d4..0000000
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
index 8aef861..ca9d5d4 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
@@ -46,7 +46,7 @@
 import androidx.compose.material3.catalog.library.model.ThemeMode
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -242,7 +242,7 @@
                     )
                 }
 
-                var fontScale by remember { mutableStateOf(theme.fontScale) }
+                var fontScale by remember { mutableFloatStateOf(theme.fontScale) }
                 FontScaleItem(
                     modifier = Modifier
                         .fillMaxWidth()
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/AndroidManifest.xml b/compose/material3/material3/integration-tests/material3-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index a7cd314..0000000
--- a/compose/material3/material3/integration-tests/material3-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/material3/material3/samples/src/main/AndroidManifest.xml b/compose/material3/material3/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index dc2f180..0000000
--- a/compose/material3/material3/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt
index 1f62b814..9cce0ba 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt
@@ -32,6 +32,7 @@
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.Assume.assumeFalse
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -64,6 +65,7 @@
     }
 
     @Test
+    @Ignore("b/272301182")
     fun darkTheme() {
         assumeFalse("See b/272301182", Build.VERSION.SDK_INT == 33)
 
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
index 24bde2e..95357cf 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
@@ -54,8 +54,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeNotNull
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -75,7 +78,7 @@
     private val OptionName = "Option 1"
 
     @Test
-    fun expandedBehaviour_expandsOnClickAndCollapsesOnOutside() {
+    fun edm_expandsOnClick_andCollapsesOnClickOutside() {
         var textFieldBounds = Rect.Zero
         rule.setMaterialContent(lightColorScheme()) {
             var expanded by remember { mutableStateOf(false) }
@@ -106,7 +109,7 @@
     }
 
     @Test
-    fun expandedBehaviour_collapseOnTextFieldClick() {
+    fun edm_collapsesOnTextFieldClick() {
         rule.setMaterialContent(lightColorScheme()) {
             var expanded by remember { mutableStateOf(true) }
             ExposedDropdownMenuForTest(
@@ -126,7 +129,39 @@
     }
 
     @Test
-    fun expandedBehaviour_expandsAndFocusesTextFieldOnTrailingIconClick() {
+    fun edm_doesNotCollapse_whenTypingOnSoftKeyboard() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(false) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it }
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).performClick()
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(TFTag).assertIsFocused()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+        val zKey = device.findObject(By.desc("z")) ?: device.findObject(By.text("z"))
+        // Only run the test if we can find a key to type, which might fail for any number of
+        // reasons (keyboard doesn't appear, unexpected locale, etc.)
+        assumeNotNull(zKey)
+
+        repeat(3) {
+            zKey.click()
+            rule.waitForIdle()
+        }
+
+        rule.onNodeWithTag(TFTag).assertTextContains("zzz")
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+    }
+
+    @Test
+    fun edm_expandsAndFocusesTextField_whenTrailingIconClicked() {
         rule.setMaterialContent(lightColorScheme()) {
             var expanded by remember { mutableStateOf(false) }
             ExposedDropdownMenuForTest(
@@ -146,7 +181,7 @@
     }
 
     @Test
-    fun expandedBehaviour_doesNotExpandIfTouchEndsOutsideBounds() {
+    fun edm_doesNotExpand_ifTouchEndsOutsideBounds() {
         var textFieldBounds = Rect.Zero
         rule.setMaterialContent(lightColorScheme()) {
             var expanded by remember { mutableStateOf(false) }
@@ -184,7 +219,7 @@
     }
 
     @Test
-    fun expandedBehaviour_doesNotExpandIfTouchIsPartOfScroll() {
+    fun edm_doesNotExpand_ifTouchIsPartOfScroll() {
         val testIndex = 2
         var textFieldSize = IntSize.Zero
         rule.setMaterialContent(lightColorScheme()) {
@@ -268,7 +303,7 @@
     }
 
     @Test
-    fun uiProperties_menuMatchesTextWidth() {
+    fun edm_widthMatchesTextFieldWidth() {
         var textFieldBounds by mutableStateOf(Rect.Zero)
         var menuBounds by mutableStateOf(Rect.Zero)
         rule.setMaterialContent(lightColorScheme()) {
@@ -294,7 +329,7 @@
     }
 
     @Test
-    fun EDMBehaviour_rightOptionIsChosen() {
+    fun edm_collapsesWithSelection_whenMenuItemClicked() {
         rule.setMaterialContent(lightColorScheme()) {
             var expanded by remember { mutableStateOf(true) }
             ExposedDropdownMenuForTest(
@@ -314,8 +349,9 @@
         rule.onNodeWithTag(TFTag).assertTextContains(OptionName)
     }
 
+    @Ignore("b/266109857")
     @Test
-    fun doesNotCrashWhenAnchorDetachedFirst() {
+    fun edm_doesNotCrash_whenAnchorDetachedFirst() {
         var parent: FrameLayout? = null
         rule.setContent {
             AndroidView(
@@ -323,12 +359,20 @@
                     FrameLayout(context).apply {
                         addView(ComposeView(context).apply {
                             setContent {
-                                Box {
-                                    ExposedDropdownMenuBox(expanded = true, onExpandedChange = {}) {
-                                        Box(
-                                            Modifier
-                                                .menuAnchor()
-                                                .size(20.dp))
+                                ExposedDropdownMenuBox(expanded = true, onExpandedChange = {}) {
+                                    TextField(
+                                        value = "Text",
+                                        onValueChange = {},
+                                        modifier = Modifier.menuAnchor().size(20.dp),
+                                    )
+                                    ExposedDropdownMenu(
+                                        expanded = true,
+                                        onDismissRequest = {},
+                                    ) {
+                                        DropdownMenuItem(
+                                            text = { Text(OptionName) },
+                                            onClick = {},
+                                        )
                                     }
                                 }
                             }
@@ -349,7 +393,7 @@
 
     @OptIn(ExperimentalMaterial3Api::class)
     @Test
-    fun withScrolledContent() {
+    fun edm_withScrolledContent() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.fillMaxSize()) {
                 ExposedDropdownMenuBox(
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
index 3c05d96..3b58b83 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
@@ -265,4 +265,4 @@
     }
 }
 
-private const val Tag = "NavigationBar"
\ No newline at end of file
+private const val Tag = "NavigationBar"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
index 91fa287..0399416 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
@@ -395,6 +395,28 @@
     }
 
     @Test
+    fun testOutlinedTextField_labelHeight_contributesToTextFieldMeasurements_whenUnfocused() {
+        val tfSize = Ref<IntSize>()
+        val labelHeight = 200.dp
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                modifier = Modifier.testTag(TextFieldTag).onGloballyPositioned {
+                    tfSize.value = it.size
+                },
+                label = {
+                    Box(Modifier.size(width = 50.dp, height = labelHeight))
+                },
+            )
+        }
+
+        rule.runOnIdleWithDensity {
+            assertThat(tfSize.value!!.height).isAtLeast(labelHeight.roundToPx())
+        }
+    }
+
+    @Test
     fun testOutlinedTextField_labelWidth_isNotAffectedByTrailingIcon_whenFocused() {
         val textFieldWidth = 100.dp
         val labelRequestedWidth = 65.dp
@@ -1135,6 +1157,34 @@
     }
 
     @Test
+    fun testOutlinedTextField_supportingText_widthIsNotWiderThanTextField() {
+        val tfSize = Ref<IntSize>()
+        val supportingSize = Ref<IntSize>()
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                modifier = Modifier.onGloballyPositioned {
+                    tfSize.value = it.size
+                },
+                supportingText = {
+                    Text(
+                        text = "Long long long long long long long long long long long long " +
+                            "long long long long long long long long long long long long",
+                        modifier = Modifier.onGloballyPositioned {
+                            supportingSize.value = it.size
+                        }
+                    )
+                }
+            )
+        }
+
+        rule.runOnIdleWithDensity {
+            assertThat(supportingSize.value!!.width).isAtMost(tfSize.value!!.width)
+        }
+    }
+
+    @Test
     fun testOutlinedTextField_supportingText_contributesToTextFieldMeasurements() {
         val tfSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
index 89dbd0d..1938541 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
@@ -30,6 +30,8 @@
 import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -605,6 +607,7 @@
     ) {
         var size: IntSize? = null
         var position: Offset? = null
+        val focusRequester = FocusRequester()
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(
                 LocalLayoutDirection provides layoutDirection,
@@ -616,6 +619,7 @@
                     BasicTextField(
                         value = value,
                         onValueChange = {},
+                        modifier = Modifier.focusRequester(focusRequester),
                         singleLine = singleLine,
                         interactionSource = interactionSource
                     ) {
@@ -656,6 +660,10 @@
             }
         }
 
+        rule.runOnUiThread {
+            focusRequester.requestFocus()
+        }
+
         rule.runOnIdle {
             with(Density) {
                 assertThat(size).isNotNull()
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt
index c0f9b3f..53c668f 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt
@@ -1121,6 +1121,34 @@
     }
 
     @Test
+    fun testTextField_supportingText_widthIsNotWiderThanTextField() {
+        val tfSize = Ref<IntSize>()
+        val supportingSize = Ref<IntSize>()
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                value = "",
+                onValueChange = {},
+                modifier = Modifier.onGloballyPositioned {
+                    tfSize.value = it.size
+                },
+                supportingText = {
+                    Text(
+                        text = "Long long long long long long long long long long long long " +
+                            "long long long long long long long long long long long long",
+                        modifier = Modifier.onGloballyPositioned {
+                            supportingSize.value = it.size
+                        }
+                    )
+                }
+            )
+        }
+
+        rule.runOnIdleWithDensity {
+            assertThat(supportingSize.value!!.width).isAtMost(tfSize.value!!.width)
+        }
+    }
+
+    @Test
     fun testTextField_supportingText_contributesToTextFieldMeasurements() {
         val tfSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
index b863609..cd1d852 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
@@ -41,6 +41,7 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -108,8 +109,8 @@
 ) {
     val density = LocalDensity.current
     val view = LocalView.current
-    var width by remember { mutableStateOf(0) }
-    var menuHeight by remember { mutableStateOf(0) }
+    var width by remember { mutableIntStateOf(0) }
+    var menuHeight by remember { mutableIntStateOf(0) }
     val verticalMarginInPx = with(density) { MenuVerticalMargin.roundToPx() }
     val coordinates = remember { Ref<LayoutCoordinates>() }
 
@@ -122,12 +123,8 @@
                     onGloballyPositioned {
                         width = it.size.width
                         coordinates.value = it
-                        updateHeight(
-                            view.rootView,
-                            coordinates.value,
-                            verticalMarginInPx
-                        ) { newHeight ->
-                            menuHeight = newHeight
+                        updateHeight(view.rootView, coordinates.value, verticalMarginInPx) {
+                            newHeight -> menuHeight = newHeight
                         }
                     }.expandable(
                         expanded = expanded,
@@ -1044,18 +1041,18 @@
 
 private fun updateHeight(
     view: View,
-    coordinates: LayoutCoordinates?,
+    anchorCoordinates: LayoutCoordinates?,
     verticalMarginInPx: Int,
     onHeightUpdate: (Int) -> Unit
 ) {
-    coordinates ?: return
+    anchorCoordinates ?: return
     val visibleWindowBounds = Rect().let {
         view.getWindowVisibleDisplayFrame(it)
         it
     }
-    val heightAbove = coordinates.boundsInWindow().top - visibleWindowBounds.top
-    val heightBelow =
-        visibleWindowBounds.bottom - visibleWindowBounds.top - coordinates.boundsInWindow().bottom
+    val heightAbove = anchorCoordinates.boundsInWindow().top - visibleWindowBounds.top
+    val heightBelow = visibleWindowBounds.bottom - visibleWindowBounds.top -
+        anchorCoordinates.boundsInWindow().bottom
     onHeightUpdate(max(heightAbove, heightBelow).toInt() - verticalMarginInPx)
 }
 
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/ExposedDropdownMenuPopup.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/ExposedDropdownMenuPopup.kt
index d18f90d..6900df5 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/ExposedDropdownMenuPopup.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/ExposedDropdownMenuPopup.kt
@@ -32,7 +32,6 @@
 import androidx.compose.runtime.CompositionContext
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -85,7 +84,6 @@
 ) {
     val view = LocalView.current
     val density = LocalDensity.current
-    val testTag = LocalPopupTestTag.current
     val layoutDirection = LocalLayoutDirection.current
     val parentComposition = rememberCompositionContext()
     val currentContent by rememberUpdatedState(content)
@@ -93,10 +91,9 @@
     val popupLayout = remember {
         PopupLayout(
             onDismissRequest = onDismissRequest,
-            testTag = testTag,
             composeView = view,
+            positionProvider = popupPositionProvider,
             density = density,
-            initialPositionProvider = popupPositionProvider,
             popupId = popupId
         ).apply {
             setContent(parentComposition) {
@@ -121,7 +118,6 @@
         popupLayout.show()
         popupLayout.updateParameters(
             onDismissRequest = onDismissRequest,
-            testTag = testTag,
             layoutDirection = layoutDirection
         )
         onDispose {
@@ -134,17 +130,10 @@
     SideEffect {
         popupLayout.updateParameters(
             onDismissRequest = onDismissRequest,
-            testTag = testTag,
             layoutDirection = layoutDirection
         )
     }
 
-    DisposableEffect(popupPositionProvider) {
-        popupLayout.positionProvider = popupPositionProvider
-        popupLayout.updatePosition()
-        onDispose {}
-    }
-
     // TODO(soboleva): Look at module arrangement so that Box can be
     //  used instead of this custom Layout
     // Get the parent's position, size and layout direction
@@ -167,11 +156,6 @@
     }
 }
 
-// TODO(b/139861182): This is a hack to work around Popups not using Semantics for test tags
-//  We should either remove it, or come up with an abstracted general solution that isn't specific
-//  to Popup
-internal val LocalPopupTestTag = compositionLocalOf { "DEFAULT_TEST_TAG" }
-
 // TODO(soboleva): Look at module dependencies so that we can get code reuse between
 // Popup's SimpleStack and Box.
 @Suppress("NOTHING_TO_INLINE")
@@ -214,21 +198,18 @@
 @SuppressLint("ViewConstructor")
 private class PopupLayout(
     private var onDismissRequest: (() -> Unit)?,
-    var testTag: String,
     private val composeView: View,
+    private val positionProvider: PopupPositionProvider,
     density: Density,
-    initialPositionProvider: PopupPositionProvider,
     popupId: UUID
 ) : AbstractComposeView(composeView.context),
     ViewRootForInspector,
     ViewTreeObserver.OnGlobalLayoutListener {
+
     private val windowManager =
         composeView.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
     private val params = createLayoutParams()
 
-    /** The logic of positioning the popup relative to its parent. */
-    var positionProvider = initialPositionProvider
-
     // Position params
     var parentLayoutDirection: LayoutDirection = LayoutDirection.Ltr
     var parentBounds: IntRect? by mutableStateOf(null)
@@ -247,15 +228,6 @@
 
     override val subCompositionView: AbstractComposeView get() = this
 
-    // Specific to exposed dropdown menus.
-    private val dismissOnOutsideClick = { offset: Offset?, bounds: IntRect ->
-        if (offset == null) false
-        else {
-            offset.x < bounds.left || offset.x > bounds.right ||
-                offset.y < bounds.top || offset.y > bounds.bottom
-        }
-    }
-
     init {
         id = android.R.id.content
         setViewTreeLifecycleOwner(composeView.findViewTreeLifecycleOwner())
@@ -304,9 +276,7 @@
         content()
     }
 
-    /**
-     * Taken from PopupWindow
-     */
+    // Taken from PopupWindow. Calls [onDismissRequest] when back button is pressed.
     override fun dispatchKeyEvent(event: KeyEvent): Boolean {
         if (event.keyCode == KeyEvent.KEYCODE_BACK) {
             if (keyDispatcherState == null) {
@@ -329,11 +299,9 @@
 
     fun updateParameters(
         onDismissRequest: (() -> Unit)?,
-        testTag: String,
         layoutDirection: LayoutDirection
     ) {
         this.onDismissRequest = onDismissRequest
-        this.testTag = testTag
         superSetLayoutDirection(layoutDirection)
     }
 
@@ -383,28 +351,18 @@
         // matter whether we return true or false as some upper layer decides on whether the
         // event is propagated to other windows or not. So for focusable the event is consumed but
         // for not focusable it is propagated to other windows.
-        if (
-            (
-                (event.action == MotionEvent.ACTION_DOWN) &&
-                    (
-                        (event.x < 0) ||
-                            (event.x >= width) ||
-                            (event.y < 0) ||
-                            (event.y >= height)
-                        )
-                ) ||
-            event.action == MotionEvent.ACTION_OUTSIDE
+        if (event.action == MotionEvent.ACTION_OUTSIDE ||
+            (event.action == MotionEvent.ACTION_DOWN &&
+                (event.x < 0 || event.x >= width || event.y < 0 || event.y >= height))
         ) {
             val parentBounds = parentBounds
             val shouldDismiss = parentBounds == null || dismissOnOutsideClick(
-                if (event.x != 0f || event.y != 0f) {
-                    Offset(
-                        params.x + event.x,
-                        params.y + event.y
-                    )
-                } else null,
+                // Keep menu open if ACTION_OUTSIDE event is reported as raw coordinates of (0, 0).
+                // This means it belongs to another owner, e.g., the soft keyboard or other window.
+                if (event.rawX != 0f && event.rawY != 0f) Offset(event.rawX, event.rawY) else null,
                 parentBounds
             )
+
             if (shouldDismiss) {
                 onDismissRequest?.invoke()
                 return true
@@ -427,6 +385,15 @@
         super.setLayoutDirection(direction)
     }
 
+    // Specific to exposed dropdown menus.
+    private fun dismissOnOutsideClick(offset: Offset?, bounds: IntRect): Boolean =
+        if (offset == null) {
+            false
+        } else {
+            offset.x < bounds.left || offset.x > bounds.right ||
+                offset.y < bounds.top || offset.y > bounds.bottom
+        }
+
     /**
      * Initialize the LayoutParams specific to [android.widget.PopupWindow].
      */
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
index 36bd57b..d85d841 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
@@ -56,7 +56,7 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.listSaver
@@ -830,7 +830,7 @@
      *
      * Use this limit to coerce the [heightOffset] value when it's updated.
      */
-    var heightOffsetLimit by mutableStateOf(initialHeightOffsetLimit)
+    var heightOffsetLimit by mutableFloatStateOf(initialHeightOffsetLimit)
 
     /**
      * The top app bar's current height offset in pixels. This height offset is applied to the fixed
@@ -857,7 +857,7 @@
      * consumes scroll events. A common implementation would update the value to be the sum of all
      * [NestedScrollConnection.onPostScroll] `consumed.y` values.
      */
-    var contentOffset by mutableStateOf(initialContentOffset)
+    var contentOffset by mutableFloatStateOf(initialContentOffset)
 
     /**
      * A value that represents the collapsed height percentage of the app bar.
@@ -905,7 +905,7 @@
         )
     }
 
-    private var _heightOffset = mutableStateOf(initialHeightOffset)
+    private var _heightOffset = mutableFloatStateOf(initialHeightOffset)
 }
 
 /**
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 4b558c6..6356636 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -304,7 +304,7 @@
 internal data class DropdownMenuPositionProvider(
     val contentOffset: DpOffset,
     val density: Density,
-    val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }
+    val onPositionCalculated: (anchorBounds: IntRect, menuBounds: IntRect) -> Unit = { _, _ -> }
 ) : PopupPositionProvider {
     override fun calculatePosition(
         anchorBounds: IntRect,
@@ -318,40 +318,45 @@
         val contentOffsetX = with(density) { contentOffset.x.roundToPx() }
         val contentOffsetY = with(density) { contentOffset.y.roundToPx() }
 
-        // Compute horizontal position.
-        val toRight = anchorBounds.left + contentOffsetX
-        val toLeft = anchorBounds.right - contentOffsetX - popupContentSize.width
-        val toDisplayRight = windowSize.width - popupContentSize.width
-        val toDisplayLeft = 0
+        // Compute menu horizontal position.
+        val leftToAnchorLeft = anchorBounds.left + contentOffsetX
+        val rightToAnchorRight = anchorBounds.right - contentOffsetX - popupContentSize.width
+        val leftToWindowLeft = 0
+        val rightToWindowRight = windowSize.width - popupContentSize.width
         val x = if (layoutDirection == LayoutDirection.Ltr) {
             sequenceOf(
-                toRight,
-                toLeft,
+                leftToAnchorLeft,
+                rightToAnchorRight,
                 // If the anchor gets outside of the window on the left, we want to position
-                // toDisplayLeft for proximity to the anchor. Otherwise, toDisplayRight.
-                if (anchorBounds.left >= 0) toDisplayRight else toDisplayLeft
+                // `leftToWindowLeft` for proximity to the anchor. Otherwise, `rightToWindowRight`.
+                if (anchorBounds.left < 0) leftToWindowLeft else rightToWindowRight
             )
         } else {
             sequenceOf(
-                toLeft,
-                toRight,
+                rightToAnchorRight,
+                leftToAnchorLeft,
                 // If the anchor gets outside of the window on the right, we want to position
-                // toDisplayRight for proximity to the anchor. Otherwise, toDisplayLeft.
-                if (anchorBounds.right <= windowSize.width) toDisplayLeft else toDisplayRight
+                // `rightToWindowRight` for proximity to the anchor. Otherwise, `leftToWindowLeft`.
+                if (anchorBounds.right > windowSize.width) rightToWindowRight else leftToWindowLeft
             )
         }.firstOrNull {
             it >= 0 && it + popupContentSize.width <= windowSize.width
-        } ?: toLeft
+        } ?: rightToAnchorRight
 
-        // Compute vertical position.
-        val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
-        val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height
-        val toCenter = anchorBounds.top - popupContentSize.height / 2
-        val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
-        val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
+        // Compute menu vertical position.
+        val topToAnchorBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
+        val bottomToAnchorTop = anchorBounds.top - contentOffsetY - popupContentSize.height
+        val centerToAnchorTop = anchorBounds.top - popupContentSize.height / 2
+        val bottomToWindowBottom = windowSize.height - popupContentSize.height - verticalMargin
+        val y = sequenceOf(
+            topToAnchorBottom,
+            bottomToAnchorTop,
+            centerToAnchorTop,
+            bottomToWindowBottom
+        ).firstOrNull {
             it >= verticalMargin &&
                 it + popupContentSize.height <= windowSize.height - verticalMargin
-        } ?: toTop
+        } ?: bottomToAnchorTop
 
         onPositionCalculated(
             anchorBounds,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
index 5f1bf66..989ceba 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
@@ -43,7 +43,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -187,7 +187,7 @@
         }
     }
 
-    var itemWidth by remember { mutableStateOf(0) }
+    var itemWidth by remember { mutableIntStateOf(0) }
 
     Box(
         modifier
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
index 4feea7b..05c8c52 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
@@ -671,16 +671,15 @@
         occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(suffixPlaceable))
 
         // measure label
-        val isLabelInMiddleSection = animationProgress < 1f
         val labelHorizontalPaddingOffset =
             paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
             paddingValues.calculateRightPadding(layoutDirection).roundToPx()
         val labelConstraints = relaxedConstraints.offset(
-            horizontal = if (isLabelInMiddleSection) {
-                -occupiedSpaceHorizontally - labelHorizontalPaddingOffset
-            } else {
-                -labelHorizontalPaddingOffset
-            },
+            horizontal = lerp(
+                -occupiedSpaceHorizontally - labelHorizontalPaddingOffset, // label in middle
+                -labelHorizontalPaddingOffset, // label at top
+                animationProgress,
+            ),
             vertical = -bottomPadding
         )
         val labelPlaceable =
@@ -715,14 +714,6 @@
                 topPadding + bottomPadding
         )
 
-        // measure supporting text
-        val supportingConstraints = relaxedConstraints.offset(
-            vertical = -occupiedSpaceVertically
-        ).copy(minHeight = 0)
-        val supportingPlaceable =
-            measurables.find { it.layoutId == SupportingId }?.measure(supportingConstraints)
-        val supportingHeight = heightOrZero(supportingPlaceable)
-
         val width =
             calculateWidth(
                 leadingPlaceableWidth = widthOrZero(leadingPlaceable),
@@ -732,21 +723,31 @@
                 textFieldPlaceableWidth = textFieldPlaceable.width,
                 labelPlaceableWidth = widthOrZero(labelPlaceable),
                 placeholderPlaceableWidth = widthOrZero(placeholderPlaceable),
-                isLabelInMiddleSection = isLabelInMiddleSection,
+                animationProgress = animationProgress,
                 constraints = constraints,
                 density = density,
                 paddingValues = paddingValues,
             )
+
+        // measure supporting text
+        val supportingConstraints = relaxedConstraints.offset(
+            vertical = -occupiedSpaceVertically
+        ).copy(minHeight = 0, maxWidth = width)
+        val supportingPlaceable =
+            measurables.find { it.layoutId == SupportingId }?.measure(supportingConstraints)
+        val supportingHeight = heightOrZero(supportingPlaceable)
+
         val totalHeight =
             calculateHeight(
-                leadingPlaceableHeight = heightOrZero(leadingPlaceable),
-                trailingPlaceableHeight = heightOrZero(trailingPlaceable),
-                prefixPlaceableHeight = heightOrZero(prefixPlaceable),
-                suffixPlaceableHeight = heightOrZero(suffixPlaceable),
-                textFieldPlaceableHeight = textFieldPlaceable.height,
-                labelPlaceableHeight = heightOrZero(labelPlaceable),
-                placeholderPlaceableHeight = heightOrZero(placeholderPlaceable),
-                supportingPlaceableHeight = heightOrZero(supportingPlaceable),
+                leadingHeight = heightOrZero(leadingPlaceable),
+                trailingHeight = heightOrZero(trailingPlaceable),
+                prefixHeight = heightOrZero(prefixPlaceable),
+                suffixHeight = heightOrZero(suffixPlaceable),
+                textFieldHeight = textFieldPlaceable.height,
+                labelHeight = heightOrZero(labelPlaceable),
+                placeholderHeight = heightOrZero(placeholderPlaceable),
+                supportingHeight = heightOrZero(supportingPlaceable),
+                animationProgress = animationProgress,
                 constraints = constraints,
                 density = density,
                 paddingValues = paddingValues,
@@ -852,7 +853,7 @@
             textFieldPlaceableWidth = textFieldWidth,
             labelPlaceableWidth = labelWidth,
             placeholderPlaceableWidth = placeholderWidth,
-            isLabelInMiddleSection = animationProgress < 1f,
+            animationProgress = animationProgress,
             constraints = ZeroConstraints,
             density = density,
             paddingValues = paddingValues,
@@ -888,14 +889,15 @@
             intrinsicMeasurer(it, width)
         } ?: 0
         return calculateHeight(
-            leadingPlaceableHeight = leadingHeight,
-            trailingPlaceableHeight = trailingHeight,
-            prefixPlaceableHeight = prefixHeight,
-            suffixPlaceableHeight = suffixHeight,
-            textFieldPlaceableHeight = textFieldHeight,
-            labelPlaceableHeight = labelHeight,
-            placeholderPlaceableHeight = placeholderHeight,
-            supportingPlaceableHeight = supportingHeight,
+            leadingHeight = leadingHeight,
+            trailingHeight = trailingHeight,
+            prefixHeight = prefixHeight,
+            suffixHeight = suffixHeight,
+            textFieldHeight = textFieldHeight,
+            labelHeight = labelHeight,
+            placeholderHeight = placeholderHeight,
+            supportingHeight = supportingHeight,
+            animationProgress = animationProgress,
             constraints = ZeroConstraints,
             density = density,
             paddingValues = paddingValues
@@ -914,7 +916,7 @@
     textFieldPlaceableWidth: Int,
     labelPlaceableWidth: Int,
     placeholderPlaceableWidth: Int,
-    isLabelInMiddleSection: Boolean,
+    animationProgress: Float,
     constraints: Constraints,
     density: Float,
     paddingValues: PaddingValues,
@@ -924,19 +926,16 @@
         textFieldPlaceableWidth + affixTotalWidth,
         placeholderPlaceableWidth + affixTotalWidth,
         // Prefix/suffix does not get applied to label
-        if (isLabelInMiddleSection) labelPlaceableWidth else 0,
+        lerp(labelPlaceableWidth, 0, animationProgress),
     )
     val wrappedWidth =
         leadingPlaceableWidth + middleSection + trailingPlaceableWidth
+
+    // Actual LayoutDirection doesn't matter; we only need the sum
+    val labelHorizontalPadding = (paddingValues.calculateLeftPadding(LayoutDirection.Ltr) +
+        paddingValues.calculateRightPadding(LayoutDirection.Ltr)).value * density
     val focusedLabelWidth =
-        if (!isLabelInMiddleSection) {
-            // Actual LayoutDirection doesn't matter; we only need the sum
-            val labelHorizontalPadding = (paddingValues.calculateLeftPadding(LayoutDirection.Ltr) +
-                paddingValues.calculateRightPadding(LayoutDirection.Ltr)).value * density
-            labelPlaceableWidth + labelHorizontalPadding.roundToInt()
-        } else {
-            0
-        }
+        ((labelPlaceableWidth + labelHorizontalPadding) * animationProgress).roundToInt()
     return maxOf(wrappedWidth, focusedLabelWidth, constraints.minWidth)
 }
 
@@ -946,40 +945,38 @@
  * inside the text field.
  */
 private fun calculateHeight(
-    leadingPlaceableHeight: Int,
-    trailingPlaceableHeight: Int,
-    prefixPlaceableHeight: Int,
-    suffixPlaceableHeight: Int,
-    textFieldPlaceableHeight: Int,
-    labelPlaceableHeight: Int,
-    placeholderPlaceableHeight: Int,
-    supportingPlaceableHeight: Int,
+    leadingHeight: Int,
+    trailingHeight: Int,
+    prefixHeight: Int,
+    suffixHeight: Int,
+    textFieldHeight: Int,
+    labelHeight: Int,
+    placeholderHeight: Int,
+    supportingHeight: Int,
+    animationProgress: Float,
     constraints: Constraints,
     density: Float,
     paddingValues: PaddingValues
 ): Int {
-    // middle section is defined as a height of the text field or placeholder (whichever is
-    // taller) plus 16.dp or half height of the label if it is taller, given that the label
-    // is vertically centered to the top edge of the resulting text field's container
-    val inputFieldHeight = max(
-        textFieldPlaceableHeight,
-        placeholderPlaceableHeight
+    val inputFieldHeight = maxOf(
+        textFieldHeight,
+        placeholderHeight,
+        lerp(labelHeight, 0, animationProgress)
     )
     val topPadding = paddingValues.calculateTopPadding().value * density
+    val actualTopPadding = lerp(topPadding, max(topPadding, labelHeight / 2f), animationProgress)
     val bottomPadding = paddingValues.calculateBottomPadding().value * density
-    val middleSectionHeight = inputFieldHeight + bottomPadding + max(
-        topPadding,
-        labelPlaceableHeight / 2f
-    )
+    val middleSectionHeight = actualTopPadding + inputFieldHeight + bottomPadding
+
     return max(
         constraints.minHeight,
         maxOf(
-            leadingPlaceableHeight,
-            trailingPlaceableHeight,
-            prefixPlaceableHeight,
-            suffixPlaceableHeight,
+            leadingHeight,
+            trailingHeight,
+            prefixHeight,
+            suffixHeight,
             middleSectionHeight.roundToInt()
-        ) + supportingPlaceableHeight
+        ) + supportingHeight
     )
 }
 
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 69c0d59..da8bcc1 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
@@ -55,6 +55,8 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -680,9 +682,9 @@
         stepsToTickFractions(steps)
     }
 
-    var startThumbWidth by remember { mutableStateOf(ThumbWidth.value) }
-    var endThumbWidth by remember { mutableStateOf(ThumbWidth.value) }
-    var totalWidth by remember { mutableStateOf(0) }
+    var startThumbWidth by remember { mutableFloatStateOf(ThumbWidth.value) }
+    var endThumbWidth by remember { mutableFloatStateOf(ThumbWidth.value) }
+    var totalWidth by remember { mutableIntStateOf(0) }
 
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
 
@@ -695,8 +697,8 @@
         scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
 
     var obtainedMeasurements = remember { mutableStateOf(false) }
-    val rawOffsetStart = remember { mutableStateOf(0f) }
-    val rawOffsetEnd = remember { mutableStateOf(0f) }
+    val rawOffsetStart = remember { mutableFloatStateOf(0f) }
+    val rawOffsetEnd = remember { mutableFloatStateOf(0f) }
 
     val gestureEndAction = rememberUpdatedState<(Boolean) -> Unit> {
         onValueChangeFinished?.invoke()
@@ -706,17 +708,17 @@
         val maxPx = max(totalWidth - endThumbWidth / 2, 0f)
         val minPx = min(startThumbWidth / 2, maxPx)
         val offsetRange = if (isStart) {
-            rawOffsetStart.value = (rawOffsetStart.value + offset)
-            rawOffsetEnd.value = scaleToOffset(minPx, maxPx, value.endInclusive)
-            val offsetEnd = rawOffsetEnd.value
-            var offsetStart = rawOffsetStart.value.coerceIn(minPx, offsetEnd)
+            rawOffsetStart.floatValue = (rawOffsetStart.floatValue + offset)
+            rawOffsetEnd.floatValue = scaleToOffset(minPx, maxPx, value.endInclusive)
+            val offsetEnd = rawOffsetEnd.floatValue
+            var offsetStart = rawOffsetStart.floatValue.coerceIn(minPx, offsetEnd)
             offsetStart = snapValueToTick(offsetStart, tickFractions, minPx, maxPx)
             offsetStart..offsetEnd
         } else {
-            rawOffsetEnd.value = (rawOffsetEnd.value + offset)
-            rawOffsetStart.value = scaleToOffset(minPx, maxPx, value.start)
-            val offsetStart = rawOffsetStart.value
-            var offsetEnd = rawOffsetEnd.value.coerceIn(offsetStart, maxPx)
+            rawOffsetEnd.floatValue = (rawOffsetEnd.floatValue + offset)
+            rawOffsetStart.floatValue = scaleToOffset(minPx, maxPx, value.start)
+            val offsetStart = rawOffsetStart.floatValue
+            var offsetEnd = rawOffsetEnd.floatValue.coerceIn(offsetStart, maxPx)
             offsetEnd = snapValueToTick(offsetEnd, tickFractions, minPx, maxPx)
             offsetStart..offsetEnd
         }
@@ -847,12 +849,12 @@
         if (!obtainedMeasurements.value) {
             val finalizedMaxPx = max(totalWidth - endThumbWidth / 2, 0f)
             val finalizedMinPx = min(startThumbWidth / 2, finalizedMaxPx)
-            rawOffsetStart.value = scaleToOffset(
+            rawOffsetStart.floatValue = scaleToOffset(
                 finalizedMinPx,
                 finalizedMaxPx,
                 value.start
             )
-            rawOffsetEnd.value = scaleToOffset(
+            rawOffsetEnd.floatValue = scaleToOffset(
                 finalizedMinPx,
                 finalizedMaxPx,
                 value.endInclusive
@@ -1679,7 +1681,7 @@
     val valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
     var onValueChangeFinished: (() -> Unit)? = null
 ) {
-    private var valueState by mutableStateOf(initialValue)
+    private var valueState by mutableFloatStateOf(initialValue)
 
     /**
      * [Float] that indicates the current value that the thumb
@@ -1709,11 +1711,11 @@
 
     internal val tickFractions = stepsToTickFractions(steps)
 
-    private var thumbWidth by mutableStateOf(ThumbWidth.value)
-    internal var totalWidth by mutableStateOf(0)
+    private var thumbWidth by mutableFloatStateOf(ThumbWidth.value)
+    internal var totalWidth by mutableIntStateOf(0)
 
-    internal var rawOffset by mutableStateOf(scaleToOffset(0f, 0f, value))
-    internal var pressOffset by mutableStateOf(0f)
+    internal var rawOffset by mutableFloatStateOf(scaleToOffset(0f, 0f, value))
+    internal var pressOffset by mutableFloatStateOf(0f)
 
     internal var isRtl = false
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt
index 1090fd0..8ebe654 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt
@@ -29,11 +29,12 @@
 import androidx.compose.material3.SwipeableDefaults.resistanceConfig
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.FloatState
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.Saver
@@ -103,20 +104,20 @@
      * You should use this state to offset your content accordingly. The recommended way is to
      * use `Modifier.offsetPx`. This includes the resistance by default, if resistance is enabled.
      */
-    val offset: State<Float> get() = offsetState
+    val offset: FloatState get() = offsetState
 
     /**
      * The amount by which the [swipeable] has been swiped past its bounds.
      */
-    val overflow: State<Float> get() = overflowState
+    val overflow: FloatState get() = overflowState
 
     // Use `Float.NaN` as a placeholder while the state is uninitialised.
-    private val offsetState = mutableStateOf(0f)
-    private val overflowState = mutableStateOf(0f)
+    private val offsetState = mutableFloatStateOf(0f)
+    private val overflowState = mutableFloatStateOf(0f)
 
     // the source of truth for the "real"(non ui) position
     // basically position in bounds + overflow
-    private val absoluteOffset = mutableStateOf(0f)
+    private val absoluteOffset = mutableFloatStateOf(0f)
 
     // current animation target, if animating, otherwise null
     private val animationTarget = mutableStateOf<Float?>(null)
@@ -138,8 +139,8 @@
             requireNotNull(initialOffset) {
                 "The initial value must have an associated anchor."
             }
-            offsetState.value = initialOffset
-            absoluteOffset.value = initialOffset
+            offsetState.floatValue = initialOffset
+            absoluteOffset.floatValue = initialOffset
         }
     }
 
@@ -174,10 +175,10 @@
                 newState ?: newAnchors.keys.minByOrNull { abs(it - animationTargetValue) }!!
             } else {
                 // we're not animating, proceed by finding the new anchors for an old value
-                val actualOldValue = oldAnchors[offset.value]
+                val actualOldValue = oldAnchors[offset.floatValue]
                 val value = if (actualOldValue == currentValue) currentValue else actualOldValue
                 newAnchors.getOffset(value) ?: newAnchors
-                    .keys.minByOrNull { abs(it - offset.value) }!!
+                    .keys.minByOrNull { abs(it - offset.floatValue) }!!
             }
             try {
                 animateInternalToOffset(targetOffset, animationSpec)
@@ -194,18 +195,18 @@
 
     internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f })
 
-    internal var velocityThreshold by mutableStateOf(0f)
+    internal var velocityThreshold by mutableFloatStateOf(0f)
 
     internal var resistance: ResistanceConfig? by mutableStateOf(null)
 
     internal val draggableState = DraggableState {
-        val newAbsolute = absoluteOffset.value + it
+        val newAbsolute = absoluteOffset.floatValue + it
         val clamped = newAbsolute.coerceIn(minBound, maxBound)
         val overflow = newAbsolute - clamped
         val resistanceDelta = resistance?.computeResistance(overflow) ?: 0f
-        offsetState.value = clamped + resistanceDelta
-        overflowState.value = overflow
-        absoluteOffset.value = newAbsolute
+        offsetState.floatValue = clamped + resistanceDelta
+        overflowState.floatValue = overflow
+        absoluteOffset.floatValue = newAbsolute
     }
 
     private suspend fun snapInternalToOffset(target: Float) {
@@ -216,7 +217,7 @@
 
     private suspend fun animateInternalToOffset(target: Float, spec: AnimationSpec<Float>) {
         draggableState.drag {
-            var prevValue = absoluteOffset.value
+            var prevValue = absoluteOffset.floatValue
             animationTarget.value = target
             isAnimationRunning = true
             try {
@@ -261,7 +262,7 @@
     @ExperimentalMaterial3Api
     internal val progress: SwipeProgress<T>
         get() {
-            val bounds = findBounds(offset.value, anchors.keys)
+            val bounds = findBounds(offset.floatValue, anchors.keys)
             val from: T
             val to: T
             val fraction: Float
@@ -285,7 +286,7 @@
                         }
                     from = anchors.getValue(a)
                     to = anchors.getValue(b)
-                    fraction = (offset.value - a) / (b - a)
+                    fraction = (offset.floatValue - a) / (b - a)
                 }
             }
             return SwipeProgress(from, to, fraction)
@@ -299,7 +300,7 @@
      */
     @ExperimentalMaterial3Api
     internal val direction: Float
-        get() = anchors.getOffset(currentValue)?.let { sign(offset.value - it) } ?: 0f
+        get() = anchors.getOffset(currentValue)?.let { sign(offset.floatValue - it) } ?: 0f
 
     /**
      * Set the state without any animation and suspend until it's set
@@ -334,7 +335,7 @@
                 }
                 animateInternalToOffset(targetOffset, anim)
             } finally {
-                val endOffset = absoluteOffset.value
+                val endOffset = absoluteOffset.floatValue
                 val endValue = anchors
                     // fighting rounding error once again, anchor should be as close as 0.5 pixels
                     .filterKeys { anchorOffset -> abs(anchorOffset - endOffset) < 0.5f }
@@ -392,9 +393,9 @@
      * @return the amount of [delta] consumed
      */
     internal fun performDrag(delta: Float): Float {
-        val potentiallyConsumed = absoluteOffset.value + delta
+        val potentiallyConsumed = absoluteOffset.floatValue + delta
         val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
-        val deltaToConsume = clamped - absoluteOffset.value
+        val deltaToConsume = clamped - absoluteOffset.floatValue
         if (abs(deltaToConsume) > 0) {
             draggableState.dispatchRawDelta(deltaToConsume)
         }
@@ -865,7 +866,7 @@
 
         override suspend fun onPreFling(available: Velocity): Velocity {
             val toFling = Offset(available.x, available.y).toFloat()
-            return if (toFling < 0 && offset.value > minBound) {
+            return if (toFling < 0 && offset.floatValue > minBound) {
                 performFling(velocity = toFling)
                 // since we go to the anchor with tween settling, consume all for the best UX
                 available
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
index 77bdf92..8db36b9 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -267,7 +268,7 @@
      * You can use this value to provide smooth reconciliation behavior when re-targeting an
      * animation.
      */
-    var lastVelocity: Float by mutableStateOf(0f)
+    var lastVelocity: Float by mutableFloatStateOf(0f)
         private set
 
     /**
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
index d13a240..1cc5891 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
@@ -700,15 +700,6 @@
             max(heightOrZero(textFieldPlaceable), heightOrZero(placeholderPlaceable)) +
                 effectiveTopOffset + bottomPaddingValue
         )
-
-        // measure supporting text
-        val supportingConstraints = looseConstraints.offset(
-            vertical = -occupiedSpaceVertically
-        ).copy(minHeight = 0)
-        val supportingPlaceable =
-            measurables.find { it.layoutId == SupportingId }?.measure(supportingConstraints)
-        val supportingHeight = heightOrZero(supportingPlaceable)
-
         val width = calculateWidth(
             leadingWidth = widthOrZero(leadingPlaceable),
             trailingWidth = widthOrZero(trailingPlaceable),
@@ -719,6 +710,15 @@
             placeholderWidth = widthOrZero(placeholderPlaceable),
             constraints = constraints,
         )
+
+        // measure supporting text
+        val supportingConstraints = looseConstraints.offset(
+            vertical = -occupiedSpaceVertically
+        ).copy(minHeight = 0, maxWidth = width)
+        val supportingPlaceable =
+            measurables.find { it.layoutId == SupportingId }?.measure(supportingConstraints)
+        val supportingHeight = heightOrZero(supportingPlaceable)
+
         val totalHeight = calculateHeight(
             textFieldHeight = textFieldPlaceable.height,
             labelHeight = heightOrZero(labelPlaceable),
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
index 6dae869..3764c75 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
@@ -91,6 +91,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -527,8 +528,12 @@
     internal var isAfternoonToggle by mutableStateOf(initialHour > 12 && !is24Hour)
     internal var isInnerCircle by mutableStateOf(initialHour >= 12)
 
-    internal var hourAngle by mutableStateOf(RadiansPerHour * initialHour % 12 - FullCircle / 4)
-    internal var minuteAngle by mutableStateOf(RadiansPerMinute * initialMinute - FullCircle / 4)
+    internal var hourAngle by mutableFloatStateOf(
+        RadiansPerHour * initialHour % 12 - FullCircle / 4
+    )
+    internal var minuteAngle by mutableFloatStateOf(
+        RadiansPerMinute * initialMinute - FullCircle / 4
+    )
 
     private val mutex = MutatorMutex()
     private val isAfternoon by derivedStateOf { is24hour && isInnerCircle || isAfternoonToggle }
@@ -1245,8 +1250,8 @@
     name = "clockDial"
     properties["state"] = state
 }) {
-    var offsetX by remember { mutableStateOf(0f) }
-    var offsetY by remember { mutableStateOf(0f) }
+    var offsetX by remember { mutableFloatStateOf(0f) }
+    var offsetY by remember { mutableFloatStateOf(0f) }
     val center by remember { mutableStateOf(IntOffset.Zero) }
     val scope = rememberCoroutineScope()
     val maxDist = with(LocalDensity.current) { MaxDistance.toPx() }
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
new file mode 100644
index 0000000..b013276
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.runtime.lint
+
+import androidx.compose.lint.Name
+import androidx.compose.lint.Names
+import androidx.compose.lint.isInPackageName
+import androidx.compose.lint.resolveCall
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
+import org.jetbrains.kotlin.psi.KtTypeArgumentList
+import org.jetbrains.kotlin.psi.KtValueArgumentList
+import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
+import org.jetbrains.uast.skipParenthesizedExprDown
+
+/**
+ * Suggests alternative functions to mutableStateOf<T>() if all of the following are true:
+ * - a snapshot mutation policy argument is not specified (or it is structural equivalent policy)
+ * - `T` is in the [replacements] map
+ * - `T` is a non-nullable type
+ */
+class AutoboxingStateCreationDetector : Detector(), SourceCodeScanner {
+
+    /**
+     * Map of canonical PSI types to the fully-qualified function that should be used to
+     * create MutableState instances of said type.
+     */
+    private val replacements = mapOf(
+        "kotlin.Int" to Names.Runtime.MutableIntStateOf,
+        "kotlin.Long" to Names.Runtime.MutableLongStateOf,
+        "kotlin.Float" to Names.Runtime.MutableFloatStateOf,
+        "kotlin.Double" to Names.Runtime.MutableDoubleStateOf,
+    )
+
+    override fun getApplicableMethodNames() = listOf(Names.Runtime.MutableStateOf.shortName)
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        if (!method.isInPackageName(Names.Runtime.PackageName)) return
+
+        val replacement = getSuggestedReplacementName(node as KotlinUFunctionCallExpression)
+            ?: return
+
+        context.report(
+            issue = AutoboxingStateCreation,
+            scope = node,
+            location = context.getNameLocation(node),
+            message = "Prefer `${replacement.shortName}` instead of `${method.name}`",
+            quickfixData = createLintFix(context, node, replacement)
+        )
+    }
+
+    private fun createLintFix(
+        context: JavaContext,
+        node: UCallExpression,
+        replacementFunction: Name
+    ): LintFix {
+        val fixes = listOfNotNull(
+            // Replace the function name
+            LintFix.create()
+                .replace()
+                .range(context.getNameLocation(node))
+                .with(replacementFunction.javaFqn)
+                .shortenNames(true)
+                .build(),
+
+            // Remove the type arguments list (if present)
+            context.getLocationOfTypeArguments(node)
+                ?.let { LintFix.create().replace().range(it).with("").build() },
+
+            // Remove the SnapshotMutationPolicy argument (if present)
+            context.getLocationOfArgumentsList(node)
+                ?.takeIf { node.getArgumentForParameter(MUTATION_POLICY_PARAM_IDX) != null }
+                ?.let { argsListLocation ->
+                    node.getArgumentForParameter(VALUE_PARAM_IDX)?.sourcePsi?.text
+                        ?.let { valueArg ->
+                            LintFix.create()
+                                .replace()
+                                .range(argsListLocation)
+                                .with("($valueArg)")
+                                .build()
+                        }
+                }
+        )
+
+        return LintFix.create()
+            .name("Replace with ${replacementFunction.shortName}")
+            .composite(*fixes.toTypedArray())
+    }
+
+    private fun JavaContext.getLocationOfTypeArguments(node: UCallExpression): Location? {
+        val typeArgsList = node.sourcePsi?.children?.firstIsInstanceOrNull<KtTypeArgumentList>()
+            ?: return null
+        return getLocation(typeArgsList)
+    }
+
+    private fun JavaContext.getLocationOfArgumentsList(node: UCallExpression): Location? {
+        val argsList = node.sourcePsi?.children?.firstIsInstanceOrNull<KtValueArgumentList>()
+            ?: return null
+        return getLocation(argsList)
+    }
+
+    private fun getSuggestedReplacementName(
+        invocation: KotlinUFunctionCallExpression
+    ): Name? {
+        if (!usesStructuralEqualityPolicy(invocation)) return null
+
+        val resolvedCall = invocation.resolveCall() ?: return null
+        val stateType = resolvedCall.typeArguments.asIterable().single().value
+        return when {
+            stateType.isMarkedNullable -> null
+            else -> replacements[stateType.getJetTypeFqName(true)]
+        }
+    }
+
+    private fun usesStructuralEqualityPolicy(
+        invocation: KotlinUFunctionCallExpression
+    ): Boolean {
+        val policyExpr = invocation.valueArguments.getOrNull(MUTATION_POLICY_PARAM_IDX)
+            ?.skipParenthesizedExprDown()
+            ?: return true // No argument passed; we're using the default policy
+
+        val policyMethod = (policyExpr as? KotlinUFunctionCallExpression)?.resolve()
+            ?: return false // Argument isn't a direct function call. Assume it's a more complex
+                            // policy, or isn't always the structural equality policy.
+
+        return policyMethod.isInPackageName(Names.Runtime.PackageName) &&
+            policyMethod.name == Names.Runtime.StructuralEqualityPolicy.shortName
+    }
+
+    companion object {
+        private const val VALUE_PARAM_IDX = 0
+        private const val MUTATION_POLICY_PARAM_IDX = 1
+
+        val AutoboxingStateCreation = Issue.create(
+            id = "AutoboxingStateCreation",
+            briefDescription = "`State<T>` will autobox values assigned to this state. " +
+                "Use a specialized state type instead.",
+            explanation = "Calling `mutableStateOf<T>()` when `T` is either backed by a " +
+                "primitive type on the JVM or is a value class results in a state implementation " +
+                "that requires all state values to be boxed. This usually causes an additional " +
+                "allocation for each state write, and adds some additional work to auto-unbox " +
+                "values when reading the value of the state. Instead, prefer to use a " +
+                "specialized primitive state implementation for `Int`, `Long`, `Float`, and " +
+                "`Double` when the state does not need to track null values and does not " +
+                "override the default `SnapshotMutationPolicy`.",
+            category = Category.PERFORMANCE, priority = 3, severity = Severity.WARNING,
+            implementation = Implementation(
+                AutoboxingStateCreationDetector::class.java,
+                EnumSet.of(Scope.JAVA_FILE)
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt
new file mode 100644
index 0000000..70fd1cd
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.runtime.lint
+
+import androidx.compose.lint.Names
+import androidx.compose.lint.isInPackageName
+import androidx.compose.lint.isVoidOrUnit
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.UastLintUtils.Companion.tryResolveUDeclaration
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiParameter
+import java.util.EnumSet
+import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UDeclarationsExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UExpression
+import org.jetbrains.uast.UExpressionList
+import org.jetbrains.uast.UParameter
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.UVariable
+import org.jetbrains.uast.skipParenthesizedExprDown
+import org.jetbrains.uast.toUElement
+
+/**
+ * Detector to warn when [Unit] is being passed "opaquely" as an argument to any of the methods in
+ * [getApplicableMethodNames]. An argument is defined as an opaque unit key if all the following
+ * are true:
+ *  - The argument is an expression of type `Unit`
+ *  - The argument is being passed to a parameter of type `Any?`
+ *  - The argument is not the `Unit` literal
+ *  - The argument is not a trivial variable or property read expression
+ */
+class OpaqueUnitKeyDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableMethodNames(): List<String> = listOf(
+        Names.Runtime.Remember.shortName,
+        Names.Runtime.RememberSaveable.shortName,
+        Names.Runtime.DisposableEffect.shortName,
+        Names.Runtime.LaunchedEffect.shortName,
+        Names.Runtime.ProduceState.shortName,
+        Names.Runtime.ReusableContent.shortName,
+        Names.Runtime.Key.shortName,
+    )
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        if (!method.isInPackageName(Names.Runtime.PackageName)) return
+
+        method.parameterList.parameters.forEach { parameter ->
+            val arg = node.getArgumentForParameter(parameter.parameterIndex())
+            if (parameter.isNullableAny()) {
+                if (arg?.isOpaqueUnitExpression() == true) {
+                    reportOpaqueUnitArgKey(
+                        context = context,
+                        method = method,
+                        methodInvocation = node,
+                        parameter = parameter,
+                        argument = arg
+                    )
+                }
+            } else if (parameter.isPotentiallyVarArgs() && arg is UExpressionList) {
+                arg.expressions.forEach { varArg ->
+                    if (varArg.isOpaqueUnitExpression()) {
+                        reportOpaqueUnitArgKey(
+                            context = context,
+                            method = method,
+                            methodInvocation = node,
+                            parameter = parameter,
+                            argument = varArg
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    private fun reportOpaqueUnitArgKey(
+        context: JavaContext,
+        method: PsiMethod,
+        methodInvocation: UCallExpression,
+        parameter: PsiParameter,
+        argument: UExpression
+    ) {
+        val rootExpression = methodInvocation.resolveRootExpression()
+        val rootExpressionLocation = context.getLocation(rootExpression)
+
+        context.report(
+            OpaqueUnitKey,
+            argument,
+            context.getLocation(argument),
+            "Implicitly passing `Unit` as argument to ${parameter.name}",
+            fix()
+                .name(
+                    "Move expression outside of `${method.name}`'s arguments " +
+                        "and pass `Unit` explicitly"
+                )
+                .composite(
+                    if (rootExpression.isInPhysicalBlock()) {
+                        // If we're in a block where we can add an expression without breaking any
+                        // syntax rules, promote the argument's expression to a sibling.
+                        fix()
+                            .replace()
+                            .range(rootExpressionLocation)
+                            .beginning()
+                            .with("${argument.asSourceString()}\n")
+                            .reformat(true)
+                            .build()
+                    } else {
+                        // If we're not in a block, then introduce one for cheap by wrapping the
+                        // call with Kotlin's `run` function to a format that appears as follows:
+                        //
+                        // ```
+                        // run {
+                        //    theArgument()
+                        //    theMethod(...)
+                        // }
+                        // ```
+                        fix()
+                            .composite(
+                                fix()
+                                    .replace()
+                                    .range(rootExpressionLocation)
+                                    .beginning()
+                                    .with("kotlin.run {\n${argument.asSourceString()}\n")
+                                    .reformat(true)
+                                    .shortenNames()
+                                    .build(),
+                                fix()
+                                    .replace()
+                                    .range(rootExpressionLocation)
+                                    .end()
+                                    .with("\n}")
+                                    .reformat(true)
+                                    .build()
+                            )
+                    },
+
+                    // Replace the old parameter with the Unit literal
+                    fix()
+                        .replace()
+                        .range(context.getLocation(argument))
+                        .with(FqUnitName)
+                        .shortenNames()
+                        .build(),
+                )
+        )
+    }
+
+    private fun UCallExpression.resolveRootExpression(): UExpression {
+        var root: UExpression = this
+        var parent: UExpression? = root.getParentExpression()
+        while (parent != null && parent !is UBlockExpression) {
+            if (!parent.isVirtual) { root = parent }
+            parent = parent.getParentExpression()
+        }
+        return root
+    }
+
+    private fun UExpression.isInPhysicalBlock(): Boolean {
+        var parent: UElement? = this
+        while (parent != null) {
+            if (parent is UBlockExpression) {
+                return !parent.isVirtual
+            }
+            parent = parent.uastParent
+        }
+        return false
+    }
+
+    private val UElement.isVirtual get() = sourcePsi == null
+
+    private fun UExpression.getParentExpression(): UExpression? {
+        return when (val parent = uastParent) {
+            is UVariable -> parent.uastParent as UDeclarationsExpression
+            is UExpression -> parent
+            else -> null
+        }
+    }
+
+    private fun PsiParameter.isNullableAny(): Boolean {
+        val element = toUElement() as UParameter
+        return element.type.canonicalText == FqJavaObjectName &&
+            element.getAnnotations().any { it.qualifiedName == FqKotlinNullableAnnotation }
+    }
+
+    private fun PsiParameter.isPotentiallyVarArgs(): Boolean {
+        return type.canonicalText == "$FqJavaObjectName[]"
+    }
+
+    private fun UExpression.isOpaqueUnitExpression(): Boolean {
+        return getExpressionType().isVoidOrUnit && !isUnitLiteral()
+    }
+
+    private fun UExpression.isUnitLiteral(): Boolean {
+        val expr = skipParenthesizedExprDown() ?: this
+        if (expr !is USimpleNameReferenceExpression) return false
+
+        return (expr.tryResolveUDeclaration() as? UClass)?.qualifiedName == FqUnitName
+    }
+
+    companion object {
+        private const val FqJavaObjectName = "java.lang.Object"
+        private const val FqUnitName = "kotlin.Unit"
+        private const val FqKotlinNullableAnnotation = "org.jetbrains.annotations.Nullable"
+
+        val OpaqueUnitKey = Issue.create(
+            "OpaqueUnitKey",
+            "Passing an expression which always returns `Unit` as a key argument",
+            "Certain Compose functions including `remember`, `LaunchedEffect`, and " +
+                "`DisposableEffect` declare (and sometimes require) one or more key parameters. " +
+                "When a key parameter changes, it is a signal that the previous invocation is " +
+                "now invalid. In certain cases, it may be required to pass `Unit` as a key to " +
+                "one of these functions, indicating that the invocation never becomes invalid. " +
+                "Using `Unit` as a key should be done infrequently, and should always be done " +
+                "explicitly by passing the `Unit` literal. This inspection checks for " +
+                "invocations where `Unit` is being passed as a key argument in any form other " +
+                "than the `Unit` literal. This is usually done by mistake, and can harm " +
+                "readability. If a Unit expression is being passed as a key, it is always " +
+                "equivalent to move the expression before the function invocation and pass the " +
+                "`Unit` literal instead.",
+            Category.CORRECTNESS, 3, Severity.WARNING,
+            Implementation(
+                OpaqueUnitKeyDetector::class.java,
+                EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
index bc749d0..1ead6c8 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
@@ -31,6 +31,7 @@
     override val minApi = CURRENT_API
     override val issues get() = listOf(
         AutoboxingStateValuePropertyDetector.AutoboxingStateValueProperty,
+        AutoboxingStateCreationDetector.AutoboxingStateCreation,
         ComposableCoroutineCreationDetector.CoroutineCreationDuringComposition,
         ComposableFlowOperatorDetector.FlowOperatorInvokedInComposition,
         ComposableLambdaParameterDetector.ComposableLambdaParameterNaming,
@@ -41,6 +42,7 @@
         MutableCollectionMutableStateDetector.MutableCollectionMutableState,
         ProduceStateDetector.ProduceStateDoesNotAssignValue,
         RememberDetector.RememberReturnType,
+        OpaqueUnitKeyDetector.OpaqueUnitKey,
         UnrememberedStateDetector.UnrememberedState
     )
     override val vendor = Vendor(
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/UnrememberedStateDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/UnrememberedStateDetector.kt
index 4eaa5a9..70ff3eb 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/UnrememberedStateDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/UnrememberedStateDetector.kt
@@ -18,9 +18,8 @@
 
 package androidx.compose.runtime.lint
 
-import androidx.compose.lint.Names
-import androidx.compose.lint.isInPackageName
 import androidx.compose.lint.isNotRemembered
+import com.android.tools.lint.client.api.UElementHandler
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Implementation
@@ -29,9 +28,9 @@
 import com.android.tools.lint.detector.api.Scope
 import com.android.tools.lint.detector.api.Severity
 import com.android.tools.lint.detector.api.SourceCodeScanner
-import com.intellij.psi.PsiMethod
-import org.jetbrains.uast.UCallExpression
 import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
 
 /**
  * [Detector] that checks `derivedStateOf`, `mutableStateOf`, `mutableStateListOf`,
@@ -39,27 +38,32 @@
  * they are `remember`ed.
  */
 class UnrememberedStateDetector : Detector(), SourceCodeScanner {
-    override fun getApplicableMethodNames(): List<String> = listOf(
-        Names.Runtime.DerivedStateOf.shortName,
-        Names.Runtime.MutableStateOf.shortName,
-        Names.Runtime.MutableStateListOf.shortName,
-        Names.Runtime.MutableStateMapOf.shortName
-    )
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UCallExpression::class.java)
+    }
 
-    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
-        if (!method.isInPackageName(Names.Runtime.PackageName)) return
-
-        if (node.isNotRemembered()) {
-            context.report(
-                UnrememberedState,
-                node,
-                context.getNameLocation(node),
-                "Creating a state object during composition without using `remember`"
-            )
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitCallExpression(node: UCallExpression) {
+                if (node.isStateFactoryInvocation() && node.isNotRemembered()) {
+                    context.report(
+                        UnrememberedState,
+                        node,
+                        context.getNameLocation(node),
+                        "Creating a state object during composition without using `remember`"
+                    )
+                }
+            }
         }
     }
 
+    private fun UCallExpression.isStateFactoryInvocation(): Boolean =
+        resolve()?.annotations?.any { it.hasQualifiedName(FqStateFactoryAnnotationName) } ?: false
+
     companion object {
+        private const val FqStateFactoryAnnotationName =
+            "androidx.compose.runtime.snapshots.StateFactoryMarker"
+
         val UnrememberedState = Issue.create(
             "UnrememberedMutableState", // Left as previous id for backwards compatibility
             "Creating a state object during composition without using `remember`",
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt
new file mode 100644
index 0000000..06eb0d1
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt
@@ -0,0 +1,505 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime.lint
+
+import androidx.compose.lint.test.Stubs
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/* ktlint-disable max-line-length */
+@RunWith(Parameterized::class)
+class AutoboxingStateCreationDetectorTest(
+    typeUnderTest: TypeUnderTest
+) : LintDetectorTest() {
+
+    private val fqType = typeUnderTest.fqName
+    private val type = typeUnderTest.typeName
+    private val stateValue = typeUnderTest.sampleValue
+
+    private val primitiveStateStub = kotlin(
+        """
+        package androidx.compose.runtime
+
+        import kotlin.reflect.KProperty
+        import $fqType
+
+        fun mutable${type}StateOf(value: $type): Mutable${type}State {
+            TODO("Not implemented in lint stubs.")
+        }
+
+        interface Mutable${type}State : State<$type> {
+            override var value: $type
+            var ${type.toLowerCaseAsciiOnly()}Value: $type
+        }
+
+        @Suppress("NOTHING_TO_INLINE")
+        inline operator fun Mutable${type}State.getValue(
+            thisObj: Any?,
+            property: KProperty<*>
+        ): $type = ${type.toLowerCaseAsciiOnly()}Value
+
+        @Suppress("NOTHING_TO_INLINE")
+        inline operator fun Mutable${type}State.setValue(
+            thisObj: Any?,
+            property: KProperty<*>,
+            value: $type
+        ) {
+            ${type.toLowerCaseAsciiOnly()}Value = value
+        }
+        """
+    )
+
+    override fun getDetector(): Detector = AutoboxingStateCreationDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(AutoboxingStateCreationDetector.AutoboxingStateCreation)
+
+    @Test
+    fun testTrivialMutableStateOf_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf<$type>($stateValue)
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        val state = mutableStateOf<$type>($stateValue)
+                                    ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         val state = mutableStateOf<$type>($stateValue)
++                         val state = mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testInferredMutableStateOf_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf($stateValue)
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        val state = mutableStateOf($stateValue)
+                                    ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         val state = mutableStateOf($stateValue)
++                         val state = mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testFqMutableStateOf_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf<$fqType>($stateValue)
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        val state = mutableStateOf<$fqType>($stateValue)
+                                    ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         val state = mutableStateOf<$fqType>($stateValue)
++                         val state = mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testStateDelegate_withExplicitType_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun propertyDelegation() {
+                        var state by mutableStateOf<$type>($stateValue)
+                        state = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        var state by mutableStateOf<$type>($stateValue)
+                                     ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         var state by mutableStateOf<$type>($stateValue)
++                         var state by mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testStateDelegate_withInferredType_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun propertyDelegation() {
+                        var state by mutableStateOf($stateValue)
+                        state = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        var state by mutableStateOf($stateValue)
+                                     ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         var state by mutableStateOf($stateValue)
++                         var state by mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testStateDelegate_withInferredType_andInternalSetter_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+                primitiveStateStub,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    class Test(initialValue: $type = $stateValue) {
+                        var state by mutableStateOf(initialValue)
+                            private set
+                    }
+                """
+                )
+            ).run().expect(
+                """
+src/androidx/compose/runtime/lint/test/Test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        var state by mutableStateOf(initialValue)
+                                     ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+            ).expectFixDiffs(
+                """
+Fix for src/androidx/compose/runtime/lint/test/Test.kt line 7: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         var state by mutableStateOf(initialValue)
++                         var state by mutable${type}StateOf(initialValue)
+            """
+            )
+    }
+
+    @Test
+    fun testStateDelegate_withTypeInferredFromProperty_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun propertyDelegation() {
+                        var state: $type by mutableStateOf($stateValue)
+                        state = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        var state: $type by mutableStateOf($stateValue)
+                                   ${" ".repeat(type.length)}    ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         var state: $type by mutableStateOf($stateValue)
++                         var state: $type by mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testStateDelegate_withNullableInferredType_cannotBeReplacedWithMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun propertyDelegation() {
+                        var state: $type? by mutableStateOf($stateValue)
+                        state = $stateValue
+                    }
+                """
+            )
+        ).run().expectClean()
+    }
+
+    @Test
+    fun testInferredMutableStateOf_withExplicitEqualityPolicy_thatCouldBeMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf($stateValue, structuralEqualityPolicy())
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expect(
+            """
+src/androidx/compose/runtime/lint/test/test.kt:8: Warning: Prefer mutable${type}StateOf instead of mutableStateOf [AutoboxingStateCreation]
+                        val state = mutableStateOf($stateValue, structuralEqualityPolicy())
+                                    ~~~~~~~~~~~~~~
+0 errors, 1 warnings
+            """
+        ).expectFixDiffs(
+            """
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 8: Replace with mutable${type}StateOf:
+@@ -8 +8
+-                         val state = mutableStateOf($stateValue, structuralEqualityPolicy())
++                         val state = mutable${type}StateOf($stateValue)
+            """
+        )
+    }
+
+    @Test
+    fun testNonStructuralEqualityPolicy_cannotBeReplacedWithMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf($stateValue, neverEqualPolicy())
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expectClean()
+    }
+
+    @Test
+    fun testNullableMutableStateOf_cannotBeReplacedWithMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf<$type?>($stateValue)
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expectClean()
+    }
+
+    @Test
+    fun testInferredNullableMutableStateOf_cannotBeReplacedWithMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state: MutableState<$type?> = mutableStateOf($stateValue)
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expectClean()
+    }
+
+    @Test
+    fun testInferredByCastNullableMutableStateOf_cannotBeReplacedWithMutablePrimitiveStateOf() {
+        lint().files(
+            primitiveStateStub,
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            kotlin(
+                """
+                    package androidx.compose.runtime.lint.test
+
+                    import androidx.compose.runtime.*
+                    import $fqType
+
+                    fun valueAssignment() {
+                        val state = mutableStateOf($stateValue as $type?)
+                        state.value = $stateValue
+                    }
+                """
+            )
+        ).run().expectClean()
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun initParameters() = listOf(
+            testCase("kotlin.Int", "42"),
+            testCase("kotlin.Long", "0xABCDEF1234"),
+            testCase("kotlin.Float", "1.5f"),
+            testCase("kotlin.Double", "1.024")
+        )
+
+        private fun testCase(fqName: String, value: String): TypeUnderTest {
+            val parts = fqName.split('.')
+            return TypeUnderTest(
+                fqName = fqName,
+                typeName = parts.last(),
+                packageName = parts.dropLast(1).joinToString(separator = "."),
+                sampleValue = value
+            )
+        }
+    }
+
+    data class TypeUnderTest(
+        val fqName: String,
+        val typeName: String,
+        val packageName: String,
+        val sampleValue: String,
+    ) {
+        // Formatting for test parameter list.
+        override fun toString() = "type = $fqName"
+    }
+}
+/* ktlint-enable max-line-length */
\ No newline at end of file
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetectorTest.kt
new file mode 100644
index 0000000..597189a
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetectorTest.kt
@@ -0,0 +1,1744 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime.lint
+
+import androidx.compose.lint.test.Stubs
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/* ktlint-disable max-line-length */
+@RunWith(JUnit4::class)
+class OpaqueUnitKeyDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = OpaqueUnitKeyDetector()
+
+    override fun getIssues() = listOf(OpaqueUnitKeyDetector.OpaqueUnitKey)
+
+    // region remember test cases
+
+    @Test
+    fun remember_withUnitLiteralKey_doesNotError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x = remember(Unit) { listOf(1, 2, 3) }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun remember_withUnitPropertyRead_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    val unitProperty = Unit
+
+                    @Composable
+                    fun Test() {
+                        val x = remember(unitProperty) { listOf(1, 2, 3) }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:10: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x = remember(unitProperty) { listOf(1, 2, 3) }
+                                         ~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 10: Move expression outside of `remember`'s arguments and pass `Unit` explicitly:
+@@ -10 +10
+-                         val x = remember(unitProperty) { listOf(1, 2, 3) }
++                         unitProperty
++ val x = remember(kotlin.Unit) { listOf(1, 2, 3) }
+                """
+            )
+    }
+
+    @Test
+    fun remember_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x = remember(produceUnit()) { listOf(1, 2, 3) }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x = remember(produceUnit()) { listOf(1, 2, 3) }
+                                         ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `remember`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x = remember(produceUnit()) { listOf(1, 2, 3) }
++                         produceUnit()
++ val x = remember(kotlin.Unit) { listOf(1, 2, 3) }
+                """
+            )
+    }
+
+    @Test
+    fun remember_withUnitComposableInvocation_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x = remember(AnotherComposable()) { listOf(1, 2, 3) }
+                    }
+
+                    @Composable
+                    fun AnotherComposable() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x = remember(AnotherComposable()) { listOf(1, 2, 3) }
+                                         ~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `remember`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x = remember(AnotherComposable()) { listOf(1, 2, 3) }
++                         AnotherComposable()
++ val x = remember(kotlin.Unit) { listOf(1, 2, 3) }
+                """
+            )
+    }
+
+    @Test
+    fun remember_withUnitComposableInvocation_reportsError_withFixInSingleExpressionFun() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun test() = remember(produceUnit()) { listOf(1, 2, 3) }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:7: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                    fun test() = remember(produceUnit()) { listOf(1, 2, 3) }
+                                          ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 7: Move expression outside of `remember`'s arguments and pass `Unit` explicitly:
+@@ -7 +7
+-                     fun test() = remember(produceUnit()) { listOf(1, 2, 3) }
++                     fun test() = kotlin.run {
++ produceUnit()
++ remember(kotlin.Unit) { listOf(1, 2, 3) }
++ }
+                """
+            )
+    }
+
+    @Test
+    fun remember_withIfStatementThatReturnsUnit_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        val x = remember(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                doSomethingElse()
+                            }
+                        ) { listOf(1, 2, 3) }
+                    }
+
+                    fun doSomething() {}
+                    fun doSomethingElse() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:9: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                            if (condition) {
+                            ^
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `remember`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x = remember(
+-                             if (condition) {
+-                                 doSomething()
+-                             } else {
+-                                 doSomethingElse()
+-                             }
++                         if (condition) {
++     doSomething()
++ }else {
++     doSomethingElse()
++ }
++ val x = remember(
++                             kotlin.Unit
+                """
+            )
+    }
+
+    @Test
+    fun remember_withIfStatementCoercedToAny_doesNotReportError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        val x = remember(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                42
+                            }
+                        ) { listOf(1, 2, 3) }
+                    }
+
+                    fun doSomething() {}
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun remember_twoKeys_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x = remember(42, produceUnit()) { listOf(1, 2, 3) }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key2 [OpaqueUnitKey]
+                        val x = remember(42, produceUnit()) { listOf(1, 2, 3) }
+                                             ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `remember`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x = remember(42, produceUnit()) { listOf(1, 2, 3) }
++                         produceUnit()
++ val x = remember(42, kotlin.Unit) { listOf(1, 2, 3) }
+                """
+            )
+    }
+
+    // endregion remember test cases
+
+    // region produceState test cases
+
+    @Test
+    fun produceState_withUnitLiteralKey_doesNotError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x by produceState("123", Unit) { /* Do nothing. */ }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun produceState_withUnitPropertyRead_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    val unitProperty = Unit
+
+                    @Composable
+                    fun Test() {
+                        val x by produceState("123", unitProperty) { /* Do nothing. */ }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:10: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x by produceState("123", unitProperty) { /* Do nothing. */ }
+                                                     ~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 10: Move expression outside of `produceState`'s arguments and pass `Unit` explicitly:
+@@ -10 +10
+-                         val x by produceState("123", unitProperty) { /* Do nothing. */ }
++                         unitProperty
++ val x by produceState("123", kotlin.Unit) { /* Do nothing. */ }
+                """
+            )
+    }
+
+    @Test
+    fun produceState_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x by produceState("123", produceUnit()) { /* Do nothing. */ }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x by produceState("123", produceUnit()) { /* Do nothing. */ }
+                                                     ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `produceState`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x by produceState("123", produceUnit()) { /* Do nothing. */ }
++                         produceUnit()
++ val x by produceState("123", kotlin.Unit) { /* Do nothing. */ }
+                """
+            )
+    }
+
+    @Test
+    fun produceState_withUnitComposableInvocation_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x by produceState("123", AnotherComposable()) { /* Do nothing. */ }
+                    }
+
+                    @Composable
+                    fun AnotherComposable() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x by produceState("123", AnotherComposable()) { /* Do nothing. */ }
+                                                     ~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `produceState`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x by produceState("123", AnotherComposable()) { /* Do nothing. */ }
++                         AnotherComposable()
++ val x by produceState("123", kotlin.Unit) { /* Do nothing. */ }
+                """
+            )
+    }
+
+    @Test
+    fun produceState_withUnitComposableInvocation_reportsError_withFixInSingleExpressionFun() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun test() = produceState("123", produceUnit()) { /* Do nothing. */ }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:7: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                    fun test() = produceState("123", produceUnit()) { /* Do nothing. */ }
+                                                     ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 7: Move expression outside of `produceState`'s arguments and pass `Unit` explicitly:
+@@ -7 +7
+-                     fun test() = produceState("123", produceUnit()) { /* Do nothing. */ }
++                     fun test() = kotlin.run {
++ produceUnit()
++ produceState("123", kotlin.Unit) { /* Do nothing. */ }
++ }
+                """
+            )
+    }
+
+    @Test
+    fun produceState_withIfStatementThatReturnsUnit_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        val x by produceState(
+                            initialValue = "123",
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                doSomethingElse()
+                            }
+                        ) { /* Do nothing. */ }
+                    }
+
+                    fun doSomething() {}
+                    fun doSomethingElse() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:10: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                            if (condition) {
+                            ^
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 10: Move expression outside of `produceState`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x by produceState(
++                         if (condition) {
++     doSomething()
++ }else {
++     doSomethingElse()
++ }
++ val x by produceState(
+-                             if (condition) {
+-                                 doSomething()
+-                             } else {
+-                                 doSomethingElse()
+-                             }
++                             kotlin.Unit
+                """
+            )
+    }
+
+    @Test
+    fun produceState_withIfStatementCoercedToAny_doesNotReportError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        val x by produceState(
+                            initialValue = "123",
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                42
+                            }
+                        ) { /* Do nothing */ }
+                    }
+
+                    fun doSomething() {}
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun produceState_twoKeys_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.SnapshotState,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        val x by produceState("123", produceUnit()) { /* Do nothing */ }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        val x by produceState("123", produceUnit()) { /* Do nothing */ }
+                                                     ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `produceState`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         val x by produceState("123", produceUnit()) { /* Do nothing */ }
++                         produceUnit()
++ val x by produceState("123", kotlin.Unit) { /* Do nothing */ }
+                """
+            )
+    }
+
+    // endregion produceState test cases
+
+    // region DisposableEffect test cases
+
+    @Test
+    fun disposableEffect_withUnitLiteralKey_doesNotError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        DisposableEffect(Unit) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun disposableEffect_withUnitPropertyRead_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    val unitProperty = Unit
+
+                    @Composable
+                    fun Test() {
+                        DisposableEffect(unitProperty) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:10: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        DisposableEffect(unitProperty) {
+                                         ~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 10: Move expression outside of `DisposableEffect`'s arguments and pass `Unit` explicitly:
+@@ -10 +10
+-                         DisposableEffect(unitProperty) {
++                         unitProperty
++ DisposableEffect(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun disposableEffect_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        DisposableEffect(produceUnit()) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        DisposableEffect(produceUnit()) {
+                                         ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `DisposableEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         DisposableEffect(produceUnit()) {
++                         produceUnit()
++ DisposableEffect(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun disposableEffect_withUnitComposableInvocation_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        DisposableEffect(AnotherComposable()) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+
+                    @Composable
+                    fun AnotherComposable() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        DisposableEffect(AnotherComposable()) {
+                                         ~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `DisposableEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         DisposableEffect(AnotherComposable()) {
++                         AnotherComposable()
++ DisposableEffect(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun disposableEffect_withUnitComposableInvocation_reportsError_withFixInSingleExpressionFun() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun test() = DisposableEffect(produceUnit()) {
+                        onDispose {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:7: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                    fun test() = DisposableEffect(produceUnit()) {
+                                                  ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 7: Move expression outside of `DisposableEffect`'s arguments and pass `Unit` explicitly:
+@@ -7 +7
+-                     fun test() = DisposableEffect(produceUnit()) {
++                     fun test() = kotlin.run {
++ produceUnit()
++ DisposableEffect(kotlin.Unit) {
+@@ -12 +14
++ }
+                """
+            )
+    }
+
+    @Test
+    fun disposableEffect_withIfStatementThatReturnsUnit_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        DisposableEffect(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                doSomethingElse()
+                            }
+                        ) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+
+                    fun doSomething() {}
+                    fun doSomethingElse() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:9: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                            if (condition) {
+                            ^
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 9: Move expression outside of `DisposableEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         DisposableEffect(
+-                             if (condition) {
+-                                 doSomething()
+-                             } else {
+-                                 doSomethingElse()
+-                             }
++                         if (condition) {
++     doSomething()
++ }else {
++     doSomethingElse()
++ }
++ DisposableEffect(
++                             kotlin.Unit
+                """
+            )
+    }
+
+    @Test
+    fun disposableEffect_withIfStatementCoercedToAny_doesNotReportError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        DisposableEffect(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                42
+                            }
+                        ) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+
+                    fun doSomething() {}
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun disposableEffect_twoKeys_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        DisposableEffect(42, produceUnit()) {
+                            onDispose {
+                                // Do nothing.
+                            }
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key2 [OpaqueUnitKey]
+                        DisposableEffect(42, produceUnit()) {
+                                             ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `DisposableEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         DisposableEffect(42, produceUnit()) {
++                         produceUnit()
++ DisposableEffect(42, kotlin.Unit) {
+                """
+            )
+    }
+
+    // endregion DisposableEffect test cases
+
+    // region LaunchedEffect test cases
+
+    @Test
+    fun launchedEffect_withUnitLiteralKey_doesNotError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        LaunchedEffect(Unit) {
+                            // Do nothing.
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun launchedEffect_withUnitPropertyRead_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    val unitProperty = Unit
+
+                    @Composable
+                    fun Test() {
+                        LaunchedEffect(unitProperty) {
+                            // Do nothing.
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:10: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        LaunchedEffect(unitProperty) {
+                                       ~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 10: Move expression outside of `LaunchedEffect`'s arguments and pass `Unit` explicitly:
+@@ -10 +10
+-                         LaunchedEffect(unitProperty) {
++                         unitProperty
++ LaunchedEffect(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun launchedEffect_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        LaunchedEffect(produceUnit()) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        LaunchedEffect(produceUnit()) {
+                                       ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `LaunchedEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         LaunchedEffect(produceUnit()) {
++                         produceUnit()
++ LaunchedEffect(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun launchedEffect_withUnitComposableInvocation_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        LaunchedEffect(AnotherComposable()) {
+                            // Do nothing.
+                        }
+                    }
+
+                    @Composable
+                    fun AnotherComposable() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                        LaunchedEffect(AnotherComposable()) {
+                                       ~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `LaunchedEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         LaunchedEffect(AnotherComposable()) {
++                         AnotherComposable()
++ LaunchedEffect(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun launchedEffect_withUnitComposableInvocation_reportsError_withFixInSingleExpressionFun() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun test() = LaunchedEffect(produceUnit()) {
+                        // Do nothing.
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:7: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                    fun test() = LaunchedEffect(produceUnit()) {
+                                                ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 7: Move expression outside of `LaunchedEffect`'s arguments and pass `Unit` explicitly:
+@@ -7 +7
+-                     fun test() = LaunchedEffect(produceUnit()) {
++                     fun test() = kotlin.run {
++ produceUnit()
++ LaunchedEffect(kotlin.Unit) {
+@@ -10 +12
++ }
+                """
+            )
+    }
+
+    @Test
+    fun launchedEffect_withIfStatementThatReturnsUnit_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        LaunchedEffect(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                doSomethingElse()
+                            }
+                        ) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun doSomething() {}
+                    fun doSomethingElse() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:9: Warning: Implicitly passing Unit as argument to key1 [OpaqueUnitKey]
+                            if (condition) {
+                            ^
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 9: Move expression outside of `LaunchedEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         LaunchedEffect(
+-                             if (condition) {
+-                                 doSomething()
+-                             } else {
+-                                 doSomethingElse()
+-                             }
++                         if (condition) {
++     doSomething()
++ }else {
++     doSomethingElse()
++ }
++ LaunchedEffect(
++                             kotlin.Unit
+                """
+            )
+    }
+
+    @Test
+    fun launchedEffect_withIfStatementCoercedToAny_doesNotReportError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        LaunchedEffect(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                42
+                            }
+                        ) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun doSomething() {}
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun launchedEffect_twoKeys_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Effects,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        LaunchedEffect(42, produceUnit()) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to key2 [OpaqueUnitKey]
+                        LaunchedEffect(42, produceUnit()) {
+                                           ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `LaunchedEffect`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         LaunchedEffect(42, produceUnit()) {
++                         produceUnit()
++ LaunchedEffect(42, kotlin.Unit) {
+                """
+            )
+    }
+
+    // endregion LaunchedEffect test cases
+
+    // region key() test cases
+
+    @Test
+    fun key_withUnitLiteralKey_doesNotError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        key(Unit) {
+                            // Do nothing.
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun key_withUnitPropertyRead_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    val unitProperty = Unit
+
+                    @Composable
+                    fun Test() {
+                        key(unitProperty) {
+                            // Do nothing.
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:10: Warning: Implicitly passing Unit as argument to keys [OpaqueUnitKey]
+                        key(unitProperty) {
+                            ~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 10: Move expression outside of `key`'s arguments and pass `Unit` explicitly:
+@@ -10 +10
+-                         key(unitProperty) {
++                         unitProperty
++ key(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun key_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        key(produceUnit()) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to keys [OpaqueUnitKey]
+                        key(produceUnit()) {
+                            ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `key`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         key(produceUnit()) {
++                         produceUnit()
++ key(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun key_withUnitComposableInvocation_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        key(AnotherComposable()) {
+                            // Do nothing.
+                        }
+                    }
+
+                    @Composable
+                    fun AnotherComposable() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to keys [OpaqueUnitKey]
+                        key(AnotherComposable()) {
+                            ~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `key`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         key(AnotherComposable()) {
++                         AnotherComposable()
++ key(kotlin.Unit) {
+                """
+            )
+    }
+
+    @Test
+    fun key_withUnitComposableInvocation_reportsError_withFixInSingleExpressionFun() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun test() = key(produceUnit()) {
+                        // Do nothing.
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:7: Warning: Implicitly passing Unit as argument to keys [OpaqueUnitKey]
+                    fun test() = key(produceUnit()) {
+                                     ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 7: Move expression outside of `key`'s arguments and pass `Unit` explicitly:
+@@ -7 +7
+-                     fun test() = key(produceUnit()) {
++                     fun test() = kotlin.run {
++ produceUnit()
++ key(kotlin.Unit) {
+@@ -10 +12
++ }
+                """
+            )
+    }
+
+    @Test
+    fun key_withIfStatementThatReturnsUnit_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        key(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                doSomethingElse()
+                            }
+                        ) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun doSomething() {}
+                    fun doSomethingElse() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:9: Warning: Implicitly passing Unit as argument to keys [OpaqueUnitKey]
+                            if (condition) {
+                            ^
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 9: Move expression outside of `key`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         key(
+-                             if (condition) {
+-                                 doSomething()
+-                             } else {
+-                                 doSomethingElse()
+-                             }
++                         if (condition) {
++     doSomething()
++ }else {
++     doSomethingElse()
++ }
++ key(
++                             kotlin.Unit
+                """
+            )
+    }
+
+    @Test
+    fun key_withIfStatementCoercedToAny_doesNotReportError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test(condition: Boolean) {
+                        key(
+                            if (condition) {
+                                doSomething()
+                            } else {
+                                42
+                            }
+                        ) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun doSomething() {}
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun key_twoKeys_withUnitFunctionCall_reportsError() {
+        lint()
+            .files(
+                Stubs.Remember,
+                Stubs.Composable,
+                Stubs.Composables,
+                kotlin(
+                    """
+                    package test
+
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun Test() {
+                        key(42, produceUnit()) {
+                            // Do nothing.
+                        }
+                    }
+
+                    fun produceUnit() {}
+                    """
+                )
+            )
+            .run()
+            .expect(
+                """
+src/test/test.kt:8: Warning: Implicitly passing Unit as argument to keys [OpaqueUnitKey]
+                        key(42, produceUnit()) {
+                                ~~~~~~~~~~~~~
+0 errors, 1 warnings
+                """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/test/test.kt line 8: Move expression outside of `key`'s arguments and pass `Unit` explicitly:
+@@ -8 +8
+-                         key(42, produceUnit()) {
++                         produceUnit()
++ key(42, kotlin.Unit) {
+                """
+            )
+    }
+
+    // endregion key() test cases
+}
+/* ktlint-enable max-line-length */
\ No newline at end of file
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedStateDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedStateDetectorTest.kt
index 77863fd..a10571f 100644
--- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedStateDetectorTest.kt
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedStateDetectorTest.kt
@@ -118,6 +118,7 @@
             ),
             Stubs.Composable,
             Stubs.SnapshotState,
+            Stubs.StateFactoryMarker,
             Stubs.Remember
         )
             .skipTestModes(TestMode.TYPE_ALIAS)
@@ -315,6 +316,7 @@
             ),
             Stubs.Composable,
             Stubs.SnapshotState,
+            Stubs.StateFactoryMarker,
             Stubs.Remember
         )
             .run()
@@ -418,5 +420,107 @@
             .run()
             .expectClean()
     }
+
+    @Test
+    fun arbitraryStateFactoryAnnotated() {
+        lint().files(
+            kotlin(
+                """
+                package androidx.compose.runtime.foo
+
+                import androidx.compose.runtime.*
+                import androidx.compose.runtime.snapshots.StateFactoryMarker
+
+                @StateFactoryMarker
+                fun makeMyState(): State<Nothing> = TODO()
+
+                @Composable
+                fun Test() {
+                    val foo = makeMyState()
+                    val bar = remember { makeMyState() }
+                }
+
+                val lambda = @Composable {
+                    val foo = makeMyState()
+                    val bar = remember { makeMyState() }
+                }
+
+                val lambda2: @Composable () -> Unit = {
+                    val foo = makeMyState()
+                    val bar = remember { makeMyState() }
+                }
+
+                @Composable
+                fun LambdaParameter(content: @Composable () -> Unit) {}
+
+                @Composable
+                fun Test2() {
+                    LambdaParameter(content = {
+                        val foo = makeMyState()
+                        val bar = remember { makeMyState() }
+                    })
+                    LambdaParameter {
+                        val foo = makeMyState()
+                        val bar = remember { makeMyState() }
+                    }
+                }
+
+                fun test3() {
+                    val localLambda1 = @Composable {
+                        val foo = makeMyState()
+                        val bar = remember { makeMyState() }
+                    }
+
+                    val localLambda2: @Composable () -> Unit = {
+                        val foo = makeMyState()
+                        val bar = remember { makeMyState() }
+                    }
+                }
+
+                @Composable
+                fun Test4() {
+                    val localObject = object {
+                        val foo = makeMyState()
+                        val bar = remember { makeMyState() }
+                    }
+                }
+            """
+            ),
+            Stubs.Composable,
+            Stubs.SnapshotState,
+            Stubs.StateFactoryMarker,
+            Stubs.Remember
+        )
+            .skipTestModes(TestMode.TYPE_ALIAS)
+            .run()
+            .expect("""
+src/androidx/compose/runtime/foo/{.kt:12: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                    val foo = makeMyState()
+                              ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:17: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                    val foo = makeMyState()
+                              ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:22: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                    val foo = makeMyState()
+                              ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:32: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                        val foo = makeMyState()
+                                  ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:36: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                        val foo = makeMyState()
+                                  ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:43: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                        val foo = makeMyState()
+                                  ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:48: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                        val foo = makeMyState()
+                                  ~~~~~~~~~~~
+src/androidx/compose/runtime/foo/{.kt:56: Error: Creating a state object during composition without using remember [UnrememberedMutableState]
+                        val foo = makeMyState()
+                                  ~~~~~~~~~~~
+8 errors, 0 warnings
+            """
+            )
+    }
 }
 /* ktlint-enable max-line-length */
diff --git a/compose/runtime/runtime-livedata/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-livedata/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/runtime/runtime-livedata/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/runtime/runtime-livedata/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime-livedata/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-livedata/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime-livedata/src/main/AndroidManifest.xml b/compose/runtime/runtime-livedata/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-livedata/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime-rxjava2/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-rxjava2/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/runtime/runtime-rxjava2/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/runtime/runtime-rxjava2/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime-rxjava2/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-rxjava2/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime-rxjava2/src/main/AndroidManifest.xml b/compose/runtime/runtime-rxjava2/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-rxjava2/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime-rxjava3/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-rxjava3/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/runtime/runtime-rxjava3/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/runtime/runtime-rxjava3/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime-rxjava3/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-rxjava3/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime-rxjava3/src/main/AndroidManifest.xml b/compose/runtime/runtime-rxjava3/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-rxjava3/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime-saveable/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-saveable/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/runtime/runtime-saveable/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/runtime/runtime-saveable/src/androidMain/AndroidManifest.xml b/compose/runtime/runtime-saveable/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime-saveable/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 0fee604..2ba2092 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -385,7 +385,7 @@
 
   public final class PrimitiveSnapshotStateKt {
     method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
   }
 
@@ -483,19 +483,19 @@
 
   public final class SnapshotDoubleStateKt {
     method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
   }
 
   public final class SnapshotIntStateKt {
     method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
   }
 
   public final class SnapshotLongStateKt {
     method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
   }
 
@@ -514,14 +514,14 @@
   public final class SnapshotStateKt {
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
     method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
     method public static inline operator <T> T getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
     method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
@@ -765,6 +765,7 @@
   public static final class Snapshot.Companion {
     method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
     method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
+    method public boolean isApplyObserverNotificationPending();
     method public void notifyObjectsInitialized();
     method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
     method public androidx.compose.runtime.snapshots.ObserverHandle registerApplyObserver(kotlin.jvm.functions.Function2<? super java.util.Set<?>,? super androidx.compose.runtime.snapshots.Snapshot,kotlin.Unit> observer);
@@ -775,6 +776,7 @@
     method public inline <R> R withMutableSnapshot(kotlin.jvm.functions.Function0<? extends R> block);
     method public inline <T> T withoutReadObservation(kotlin.jvm.functions.Function0<? extends T> block);
     property public final androidx.compose.runtime.snapshots.Snapshot current;
+    property public final boolean isApplyObserverNotificationPending;
   }
 
   public final class SnapshotApplyConflictException extends java.lang.Exception {
@@ -885,6 +887,9 @@
     method @Deprecated public void withNoObservations(kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface StateFactoryMarker {
+  }
+
   @kotlin.jvm.JvmDefaultWithCompatibility public interface StateObject {
     method public androidx.compose.runtime.snapshots.StateRecord getFirstStateRecord();
     method public default androidx.compose.runtime.snapshots.StateRecord? mergeRecords(androidx.compose.runtime.snapshots.StateRecord previous, androidx.compose.runtime.snapshots.StateRecord current, androidx.compose.runtime.snapshots.StateRecord applied);
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 1fa82d6..6edfaf4 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -433,7 +433,7 @@
 
   public final class PrimitiveSnapshotStateKt {
     method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
   }
 
@@ -532,19 +532,19 @@
 
   public final class SnapshotDoubleStateKt {
     method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
   }
 
   public final class SnapshotIntStateKt {
     method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
   }
 
   public final class SnapshotLongStateKt {
     method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
   }
 
@@ -563,14 +563,14 @@
   public final class SnapshotStateKt {
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
     method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
     method public static inline operator <T> T getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
     method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
@@ -833,6 +833,7 @@
   public static final class Snapshot.Companion {
     method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
     method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
+    method public boolean isApplyObserverNotificationPending();
     method public void notifyObjectsInitialized();
     method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
     method @androidx.compose.runtime.InternalComposeApi public int openSnapshotCount();
@@ -844,6 +845,7 @@
     method public inline <R> R withMutableSnapshot(kotlin.jvm.functions.Function0<? extends R> block);
     method public inline <T> T withoutReadObservation(kotlin.jvm.functions.Function0<? extends T> block);
     property public final androidx.compose.runtime.snapshots.Snapshot current;
+    property public final boolean isApplyObserverNotificationPending;
   }
 
   public final class SnapshotApplyConflictException extends java.lang.Exception {
@@ -965,6 +967,9 @@
     method @Deprecated public void withNoObservations(kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface StateFactoryMarker {
+  }
+
   @kotlin.jvm.JvmDefaultWithCompatibility public interface StateObject {
     method public androidx.compose.runtime.snapshots.StateRecord getFirstStateRecord();
     method public default androidx.compose.runtime.snapshots.StateRecord? mergeRecords(androidx.compose.runtime.snapshots.StateRecord previous, androidx.compose.runtime.snapshots.StateRecord current, androidx.compose.runtime.snapshots.StateRecord applied);
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 5ce4dbc..fae054f 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -417,7 +417,7 @@
 
   public final class PrimitiveSnapshotStateKt {
     method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
   }
 
@@ -519,19 +519,19 @@
 
   public final class SnapshotDoubleStateKt {
     method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
   }
 
   public final class SnapshotIntStateKt {
     method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
   }
 
   public final class SnapshotLongStateKt {
     method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
     method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
   }
 
@@ -550,14 +550,14 @@
   public final class SnapshotStateKt {
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
     method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
     method public static inline operator <T> T getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+    method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
     method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
@@ -806,6 +806,7 @@
     method @kotlin.PublishedApi internal androidx.compose.runtime.snapshots.Snapshot createNonObservableSnapshot();
     method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
     method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
+    method public boolean isApplyObserverNotificationPending();
     method public void notifyObjectsInitialized();
     method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
     method public androidx.compose.runtime.snapshots.ObserverHandle registerApplyObserver(kotlin.jvm.functions.Function2<? super java.util.Set<?>,? super androidx.compose.runtime.snapshots.Snapshot,kotlin.Unit> observer);
@@ -818,6 +819,7 @@
     method public inline <R> R withMutableSnapshot(kotlin.jvm.functions.Function0<? extends R> block);
     method public inline <T> T withoutReadObservation(kotlin.jvm.functions.Function0<? extends T> block);
     property public final androidx.compose.runtime.snapshots.Snapshot current;
+    property public final boolean isApplyObserverNotificationPending;
   }
 
   public final class SnapshotApplyConflictException extends java.lang.Exception {
@@ -935,6 +937,9 @@
     method @Deprecated public void withNoObservations(kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface StateFactoryMarker {
+  }
+
   @kotlin.jvm.JvmDefaultWithCompatibility public interface StateObject {
     method public androidx.compose.runtime.snapshots.StateRecord getFirstStateRecord();
     method public default androidx.compose.runtime.snapshots.StateRecord? mergeRecords(androidx.compose.runtime.snapshots.StateRecord previous, androidx.compose.runtime.snapshots.StateRecord current, androidx.compose.runtime.snapshots.StateRecord applied);
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml b/compose/runtime/runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index df93a1a..0000000
--- a/compose/runtime/runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
diff --git a/compose/runtime/runtime/integration-tests/build.gradle b/compose/runtime/runtime/integration-tests/build.gradle
index b9b65e5..f62e6ba 100644
--- a/compose/runtime/runtime/integration-tests/build.gradle
+++ b/compose/runtime/runtime/integration-tests/build.gradle
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
@@ -24,69 +23,63 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        androidTestImplementation(projectOrArtifact(":compose:ui:ui"))
-        androidTestImplementation(projectOrArtifact(":compose:material:material"))
-        androidTestImplementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
-        androidTestImplementation(project(":compose:runtime:runtime"))
-        androidTestImplementation(projectOrArtifact(":compose:test-utils"))
-        androidTestImplementation(projectOrArtifact(":activity:activity-compose"))
-
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.kotlinTestJunit)
-        androidTestImplementation(libs.testExtJunit)
-        androidTestImplementation(libs.testCore)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.kotlinStdlib)
-        androidTestImplementation(libs.kotlinReflect)
-        androidTestImplementation(libs.truth)
-    }
-}
-
-android {
-    namespace "androidx.compose.runtime.integrationtests"
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
                 implementation(projectOrArtifact(":compose:ui:ui"))
             }
-            jvmMain.dependencies {
+        }
+
+        commonTest {
+            dependencies {
+                implementation(kotlin("test-junit"))
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
                 implementation(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
             }
-            androidMain.dependencies {
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api(libs.kotlinCoroutinesAndroid)
                 api("androidx.annotation:annotation:1.1.0")
 
                 implementation("androidx.core:core-ktx:1.1.0")
             }
-            desktopMain.dependencies {
-                api(libs.kotlinCoroutinesSwing)
-            }
+        }
 
-            commonTest.dependencies {
-                implementation(kotlin("test-junit"))
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    api(libs.kotlinCoroutinesSwing)
+                }
             }
-            androidAndroidTest.dependencies {
+        }
+
+        jvmTest {
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(projectOrArtifact(":compose:ui:ui"))
                 implementation(projectOrArtifact(":compose:material:material"))
                 implementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
@@ -98,9 +91,23 @@
                 implementation(libs.truth)
             }
         }
+
+        androidTest {
+            dependsOn(jvmTest)
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+            }
+        }
     }
 }
 
+android {
+    namespace "androidx.compose.runtime.integrationtests"
+}
+
 tasks.withType(KotlinCompile).configureEach {
     kotlinOptions {
         incremental = false
diff --git a/compose/runtime/runtime/integration-tests/src/androidMain/AndroidManifest.xml b/compose/runtime/runtime/integration-tests/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/runtime/runtime/integration-tests/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/runtime/runtime/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 67aa632..0000000
--- a/compose/runtime/runtime/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<manifest />
diff --git a/compose/runtime/runtime/src/androidMain/AndroidManifest.xml b/compose/runtime/runtime/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index b47339d..0000000
--- a/compose/runtime/runtime/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index 7aade6c..06356b3 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -673,35 +673,39 @@
 
     override fun prepareCompose(block: () -> Unit) = composer.prepareCompose(block)
 
-    private fun addPendingInvalidationsLocked(values: Set<Any>, forgetConditionalScopes: Boolean) {
-        var invalidated: HashSet<RecomposeScopeImpl>? = null
-
-        fun invalidate(value: Any) {
-            observations.forEachScopeOf(value) { scope ->
-                if (
-                    !observationsProcessed.remove(value, scope) &&
-                    scope.invalidateForResult(value) != InvalidationResult.IGNORED
-                ) {
-                    if (scope.isConditional && !forgetConditionalScopes) {
-                        conditionallyInvalidatedScopes.add(scope)
-                    } else {
-                        val set = invalidated
-                            ?: HashSet<RecomposeScopeImpl>().also {
-                                invalidated = it
-                            }
-                        set.add(scope)
-                    }
+    private fun HashSet<RecomposeScopeImpl>?.addPendingInvalidationsLocked(
+        value: Any,
+        forgetConditionalScopes: Boolean
+    ): HashSet<RecomposeScopeImpl>? {
+        var set = this
+        observations.forEachScopeOf(value) { scope ->
+            if (
+                !observationsProcessed.remove(value, scope) &&
+                scope.invalidateForResult(value) != InvalidationResult.IGNORED
+            ) {
+                if (scope.isConditional && !forgetConditionalScopes) {
+                    conditionallyInvalidatedScopes.add(scope)
+                } else {
+                    if (set == null) set = HashSet()
+                    set?.add(scope)
                 }
             }
         }
+        return set
+    }
+
+    private fun addPendingInvalidationsLocked(values: Set<Any>, forgetConditionalScopes: Boolean) {
+        var invalidated: HashSet<RecomposeScopeImpl>? = null
 
         values.fastForEach { value ->
             if (value is RecomposeScopeImpl) {
                 value.invalidateForResult(null)
             } else {
-                invalidate(value)
+                invalidated =
+                    invalidated.addPendingInvalidationsLocked(value, forgetConditionalScopes)
                 derivedStates.forEachScopeOf(value) {
-                    invalidate(it)
+                    invalidated =
+                        invalidated.addPendingInvalidationsLocked(it, forgetConditionalScopes)
                 }
             }
         }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
index 4fa3504..a131262 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.collection.IdentityArrayMap
 import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.runtime.snapshots.StateFactoryMarker
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.current
@@ -279,6 +280,7 @@
  *
  * @param calculation the calculation to create the value this state object represents.
  */
+@StateFactoryMarker
 fun <T> derivedStateOf(
     calculation: () -> T,
 ): State<T> = DerivedSnapshotState(calculation, null)
@@ -296,6 +298,7 @@
  * @param policy mutation policy to control when changes to the [calculation] result trigger update.
  * @param calculation the calculation to create the value this state object represents.
  */
+@StateFactoryMarker
 fun <T> derivedStateOf(
     policy: SnapshotMutationPolicy<T>,
     calculation: () -> T,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index cc4584c..faf8739 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -612,7 +612,12 @@
 
                         // Perform apply changes
                         try {
-                            toComplete += toApply
+                            // We could do toComplete += toApply but doing it like below
+                            // avoids unncessary allocations since toApply is a mutable list
+                            // toComplete += toApply
+                            toApply.fastForEach { composition ->
+                                toComplete.add(composition)
+                            }
                             toApply.fastForEach { composition ->
                                 composition.applyChanges()
                             }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
index f4fb14b..8521c5a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
@@ -22,6 +22,7 @@
 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotMutableState
+import androidx.compose.runtime.snapshots.StateFactoryMarker
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.overwritable
@@ -46,6 +47,7 @@
  * @see mutableLongStateOf
  * @see mutableFloatStateOf
  */
+@StateFactoryMarker
 fun mutableDoubleStateOf(
     value: Double
 ): MutableDoubleState = createSnapshotMutableDoubleState(value)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
index df4a2f7..fa6bfe5 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotMutableState
+import androidx.compose.runtime.snapshots.StateFactoryMarker
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.overwritable
@@ -45,6 +46,7 @@
  * @see mutableLongStateOf
  * @see mutableDoubleStateOf
  */
+@StateFactoryMarker
 fun mutableFloatStateOf(
     value: Float
 ): MutableFloatState = createSnapshotMutableFloatState(value)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
index ec700ee..e43f884 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotMutableState
+import androidx.compose.runtime.snapshots.StateFactoryMarker
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.overwritable
@@ -45,6 +46,7 @@
  * @see mutableFloatStateOf
  * @see mutableDoubleStateOf
  */
+@StateFactoryMarker
 fun mutableIntStateOf(
     value: Int
 ): MutableIntState = createSnapshotMutableIntState(value)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
index a95ae2e..a22c5c4 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
@@ -22,6 +22,7 @@
 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotMutableState
+import androidx.compose.runtime.snapshots.StateFactoryMarker
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.overwritable
@@ -46,6 +47,7 @@
  * @see mutableFloatStateOf
  * @see mutableDoubleStateOf
  */
+@StateFactoryMarker
 fun mutableLongStateOf(
     value: Long
 ): MutableLongState = createSnapshotMutableLongState(value)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
index d408eb3..60e2885 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
@@ -22,6 +22,7 @@
 import androidx.compose.runtime.snapshots.SnapshotMutableState
 import androidx.compose.runtime.snapshots.SnapshotStateList
 import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.runtime.snapshots.StateFactoryMarker
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.overwritable
@@ -53,6 +54,7 @@
  * @see mutableFloatStateOf
  * @see mutableDoubleStateOf
  */
+@StateFactoryMarker
 fun <T> mutableStateOf(
     value: T,
     policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
@@ -223,6 +225,7 @@
  * @see MutableList
  * @see Snapshot.takeSnapshot
  */
+@StateFactoryMarker
 fun <T> mutableStateListOf() = SnapshotStateList<T>()
 
 /**
@@ -233,6 +236,7 @@
  * @see MutableList
  * @see Snapshot.takeSnapshot
  */
+@StateFactoryMarker
 fun <T> mutableStateListOf(vararg elements: T) =
     SnapshotStateList<T>().also { it.addAll(elements.toList()) }
 
@@ -252,6 +256,7 @@
  * @see MutableMap
  * @see Snapshot.takeSnapshot
  */
+@StateFactoryMarker
 fun <K, V> mutableStateMapOf() = SnapshotStateMap<K, V>()
 
 /**
@@ -262,6 +267,7 @@
  * @see MutableMap
  * @see Snapshot.takeSnapshot
  */
+@StateFactoryMarker
 fun <K, V> mutableStateMapOf(vararg pairs: Pair<K, V>) =
     SnapshotStateMap<K, V>().apply { putAll(pairs.toMap()) }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
index 06d1f47..d5d312b 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.runtime.snapshots
 
+import androidx.compose.runtime.AtomicInt
 import androidx.compose.runtime.AtomicReference
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisallowComposableCalls
@@ -280,6 +281,13 @@
         val current get() = currentSnapshot()
 
         /**
+         * Returns whether any threads are currently in the process of notifying observers about
+         * changes to the global snapshot.
+         */
+        val isApplyObserverNotificationPending: Boolean
+            get() = pendingApplyObserverCount.get() > 0
+
+        /**
          * Take a snapshot of the current value of all state objects. The values are preserved until
          * [Snapshot.dispose] is called on the result.
          *
@@ -1767,20 +1775,36 @@
     return result
 }
 
+/**
+ * Counts the number of threads currently inside `advanceGlobalSnapshot`, notifying observers of
+ * changes to the global snapshot.
+ */
+private var pendingApplyObserverCount = AtomicInt(0)
+
 private fun <T> advanceGlobalSnapshot(block: (invalid: SnapshotIdSet) -> T): T {
     var previousGlobalSnapshot = snapshotInitializer as GlobalSnapshot
+
+    var modified: IdentityArraySet<StateObject>? = null // Effectively val; can be with contracts
     val result = sync {
         previousGlobalSnapshot = currentGlobalSnapshot.get()
+        modified = previousGlobalSnapshot.modified
+        if (modified != null) {
+            pendingApplyObserverCount.add(1)
+        }
         takeNewGlobalSnapshot(previousGlobalSnapshot, block)
     }
 
     // If the previous global snapshot had any modified states then notify the registered apply
     // observers.
-    val modified = previousGlobalSnapshot.modified
-    if (modified != null) {
-        val observers: List<(Set<Any>, Snapshot) -> Unit> = sync { applyObservers.toMutableList() }
-        observers.fastForEach { observer ->
-            observer(modified, previousGlobalSnapshot)
+    modified?.let {
+        try {
+            val observers: List<(Set<Any>, Snapshot) -> Unit> =
+                sync { applyObservers.toMutableList() }
+            observers.fastForEach { observer ->
+                observer(it, previousGlobalSnapshot)
+            }
+        } finally {
+            pendingApplyObserverCount.add(-1)
         }
     }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateFactoryMarker.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateFactoryMarker.kt
new file mode 100644
index 0000000..7555423
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateFactoryMarker.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime.snapshots
+
+/**
+ * Designates a function as a factory function that produces a State object. Since State factory
+ * functions create and initialize a state, they should usually only be called once per state value
+ * initialization. This annotation causes all invocations of marked state factory functions to be
+ * checked to ensure that they are not called directly inside the body of a [Composable] function
+ * and are instead wrapped with an appropriate [remember] call.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.BINARY)
+annotation class StateFactoryMarker
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
index 8efc203..3ec3fb7 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
@@ -269,6 +269,33 @@
     }
 
     @Test
+    fun applyObserverNotificationIsPendingWhileSendingApplyNotifications() {
+        val state = mutableStateOf(0)
+
+        var notificationsPendingWhileObserving = false
+        val unregister = Snapshot.registerApplyObserver { _, _ ->
+            notificationsPendingWhileObserving = Snapshot.isApplyObserverNotificationPending
+        }
+
+        try {
+            // Normally not pending
+            assertFalse(Snapshot.isApplyObserverNotificationPending)
+
+            state.value = 1
+
+            Snapshot.sendApplyNotifications()
+
+            // Was pending while sending apply notifications
+            assertTrue(notificationsPendingWhileObserving)
+
+            // Not pending afterwards
+            assertFalse(Snapshot.isApplyObserverNotificationPending)
+        } finally {
+            unregister.dispose()
+        }
+    }
+
+    @Test
     fun aNestedSnapshotCanBeTaken() {
         val state = mutableStateOf<Int>(0)
 
diff --git a/compose/ui/ui-android-stubs/src/main/AndroidManifest.xml b/compose/ui/ui-android-stubs/src/main/AndroidManifest.xml
deleted file mode 100644
index ee19b21..0000000
--- a/compose/ui/ui-android-stubs/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-geometry/src/androidMain/AndroidManifest.xml b/compose/ui/ui-geometry/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index ee19b21..0000000
--- a/compose/ui/ui-geometry/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index ae86521..55136ac 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -401,7 +401,7 @@
   }
 
   public fun interface ColorProducer {
-    method public long invoke();
+    method public operator long invoke();
   }
 
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class FilterQuality {
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
index d6fde28..6ee1568 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -401,7 +401,7 @@
   }
 
   public fun interface ColorProducer {
-    method public long invoke();
+    method public operator long invoke();
   }
 
   @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGraphicsApi {
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index 17269b9..785b8d7 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -432,7 +432,7 @@
   }
 
   public fun interface ColorProducer {
-    method public long invoke();
+    method public operator long invoke();
   }
 
   public final class DegreesKt {
diff --git a/compose/ui/ui-graphics/benchmark/test/src/androidTest/AndroidManifest.xml b/compose/ui/ui-graphics/benchmark/test/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e865f27..0000000
--- a/compose/ui/ui-graphics/benchmark/test/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/ui/ui-graphics/build.gradle b/compose/ui/ui-graphics/build.gradle
index 815c1df..cb9f0da 100644
--- a/compose/ui/ui-graphics/build.gradle
+++ b/compose/ui/ui-graphics/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,102 +23,72 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    dependencies {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-        api("androidx.annotation:annotation:1.2.0")
-        api(project(":compose:ui:ui-unit"))
-
-        implementation("androidx.compose.runtime:runtime:1.2.1")
-        implementation(project(":compose:ui:ui-util"))
-        implementation(libs.kotlinStdlibCommon)
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.kotlinTestJunit)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-
-        androidTestImplementation(project(":compose:ui:ui-graphics:ui-graphics-samples"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.espressoCore)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-
-        lintPublish(project(":compose:ui:ui-graphics-lint"))
-
-        samples(projectOrArtifact(":compose:ui:ui-graphics:ui-graphics-samples"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
 
                 api(project(":compose:ui:ui-unit"))
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            androidMain.dependencies {
-                api("androidx.annotation:annotation:1.2.0")
+        commonTest {
+            dependencies {
+                implementation(kotlin("test"))
             }
+        }
 
+        if (desktopEnabled) {
             skikoMain {
                 dependsOn(commonMain)
                 dependencies {
                     api(libs.skikoCommon)
+                    implementation(project(":compose:runtime:runtime"))
                 }
             }
+        }
 
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api("androidx.annotation:annotation:1.2.0")
+            }
+        }
+
+        if (desktopEnabled) {
             desktopMain {
-                dependsOn skikoMain
+                dependsOn(jvmMain)
+                dependsOn(skikoMain)
                 dependencies {
                     implementation(libs.kotlinStdlib)
                     implementation(libs.kotlinStdlibJdk8)
                 }
             }
+        }
 
-            commonTest {
-                dependencies {
-                    implementation(kotlin("test"))
-                }
+        jvmTest {
+            dependencies {
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
-            }
-
-            androidAndroidTest.dependencies {
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:ui:ui-graphics:ui-graphics-samples"))
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation(project(":compose:test-utils"))
@@ -128,9 +97,26 @@
                 implementation(libs.espressoCore)
                 implementation(libs.junit)
             }
+        }
 
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+                implementation(libs.truth)
+            }
+        }
+
+        if (desktopEnabled) {
             desktopTest {
                 resources.srcDirs += "src/desktopTest/res"
+                dependsOn(jvmTest)
                 dependencies {
                     implementation(project(":compose:ui:ui-test-junit4"))
                     implementation(libs.junit)
@@ -140,9 +126,10 @@
             }
         }
     }
-    dependencies {
-        samples(projectOrArtifact(":compose:ui:ui-graphics:ui-graphics-samples"))
-    }
+}
+
+dependencies {
+    lintPublish(project(":compose:ui:ui-graphics-lint"))
 }
 
 androidx {
@@ -161,7 +148,7 @@
     namespace "androidx.compose.ui.graphics"
 }
 
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
+if (desktopEnabled) {
     tasks.findByName("desktopTest").configure {
         systemProperties["GOLDEN_PATH"] = project.rootDir.absolutePath + "/../../golden"
     }
diff --git a/compose/ui/ui-graphics/samples/src/main/AndroidManifest.xml b/compose/ui/ui-graphics/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/ui/ui-graphics/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/ui/ui-graphics/src/androidMain/AndroidManifest.xml b/compose/ui/ui-graphics/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index ee19b21..0000000
--- a/compose/ui/ui-graphics/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
index ca53498..bf442bc 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
@@ -668,5 +668,5 @@
     /**
      * Return the color
      */
-    fun invoke(): Color
+    operator fun invoke(): Color
 }
\ No newline at end of file
diff --git a/compose/ui/ui-inspection/src/main/AndroidManifest.xml b/compose/ui/ui-inspection/src/main/AndroidManifest.xml
deleted file mode 100644
index f3ba9c7..0000000
--- a/compose/ui/ui-inspection/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
\ No newline at end of file
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index 5e4501d..5db2d70 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,81 +23,42 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-android {
-    lintOptions {
-        disable("InvalidPackage")
-    }
-    namespace "androidx.compose.ui.test.junit4"
-}
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-dependencies {
-
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        api(project(":compose:ui:ui-test"))
-        api("androidx.activity:activity:1.2.1")
-        api(libs.junit)
-        api(libs.kotlinStdlib)
-        api(libs.kotlinStdlibCommon)
-        api(libs.testExtJunit)
-
-        implementation("androidx.compose.runtime:runtime-saveable:1.2.1")
-        implementation("androidx.activity:activity-compose:1.3.0")
-        implementation("androidx.annotation:annotation:1.1.0")
-        implementation("androidx.lifecycle:lifecycle-common:2.5.1")
-        implementation("androidx.lifecycle:lifecycle-runtime:2.5.1")
-        implementation("androidx.test:core:1.5.0")
-        implementation("androidx.test:monitor:1.6.0")
-        implementation("androidx.test.espresso:espresso-core:3.5.0")
-        implementation("androidx.test.espresso:espresso-idling-resource:3.5.0")
-        implementation(libs.kotlinCoroutinesCore)
-        implementation(libs.kotlinCoroutinesTest)
-
-        testImplementation(project(":compose:animation:animation-core"))
-        testImplementation(project(":compose:material:material"))
-        testImplementation(project(":compose:test-utils"))
-        testImplementation(libs.truth)
-        testImplementation(libs.robolectric)
-
-        androidTestImplementation(project(":compose:animation:animation"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":compose:material:material"))
-        androidTestImplementation("androidx.fragment:fragment-testing:1.4.1")
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoKotlin)
-    }
-}
-
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 api(project(":compose:ui:ui-test"))
                 implementation(libs.kotlinStdlib)
                 implementation(libs.kotlinCoroutinesCore)
                 implementation(libs.kotlinCoroutinesTest)
             }
+        }
 
-            jvmMain.dependencies {
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
                 api(libs.junit)
                 api(libs.kotlinStdlib)
                 api(libs.kotlinStdlibCommon)
 
                 compileOnly("androidx.annotation:annotation:1.1.0")
             }
+        }
 
-            androidMain.dependencies {
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.activity:activity:1.2.1")
                 implementation "androidx.activity:activity-compose:1.3.0"
                 api(libs.testExtJunit)
@@ -107,24 +67,31 @@
                 implementation(project(":compose:runtime:runtime-saveable"))
                 implementation("androidx.lifecycle:lifecycle-common:2.5.1")
                 implementation("androidx.lifecycle:lifecycle-runtime:2.5.1")
-                implementation("androidx.test:core:1.4.0")
+                implementation("androidx.test:core:1.5.0")
                 implementation(libs.testMonitor)
                 implementation("androidx.test.espresso:espresso-core:3.3.0")
-                implementation("androidx.test.espresso:espresso-idling-resource:3.3.0")
+                implementation("androidx.test.espresso:espresso-idling-resource:3.5.0")
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(project(":compose:animation:animation-core"))
-                implementation(project(":compose:material:material"))
-                implementation(project(":compose:test-utils"))
-                implementation(libs.truth)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.truth)
+                    implementation(libs.skiko)
+                }
             }
+        }
 
-            androidAndroidTest.dependencies {
+        jvmTest {
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:animation:animation"))
                 implementation(project(":compose:test-utils"))
                 implementation(project(":compose:material:material"))
@@ -136,28 +103,50 @@
                 implementation(libs.dexmakerMockito)
                 implementation(libs.mockitoKotlin)
             }
+        }
 
-            desktopMain.dependencies {
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(project(":compose:animation:animation-core"))
+                implementation(project(":compose:material:material"))
+                implementation(project(":compose:test-utils"))
                 implementation(libs.truth)
-                implementation(libs.skiko)
             }
+        }
 
-            desktopTest.dependencies {
-                implementation(libs.truth)
-                implementation(libs.junit)
-                implementation(libs.kotlinTest)
-                implementation(libs.skikoCurrentOs)
-                implementation(project(":compose:foundation:foundation"))
-                implementation(project(":compose:ui:ui-test-junit4"))
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependencies {
+                    implementation(libs.truth)
+                    implementation(libs.junit)
+                    implementation(libs.kotlinTest)
+                    implementation(libs.skikoCurrentOs)
+                    implementation(project(":compose:foundation:foundation"))
+                    implementation(project(":compose:ui:ui-test-junit4"))
+                }
             }
         }
     }
+}
 
-    dependencies {
-        // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
-        // leaks into instrumented tests (b/214407011)
-        testImplementation(libs.robolectric)
+
+android {
+    lintOptions {
+        disable("InvalidPackage")
     }
+    namespace "androidx.compose.ui.test.junit4"
+}
+
+dependencies {
+    // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
+    // leaks into instrumented tests (b/214407011)
+    testImplementation(libs.robolectric)
 }
 
 androidx {
diff --git a/compose/ui/ui-test-junit4/src/androidMain/AndroidManifest.xml b/compose/ui/ui-test-junit4/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/compose/ui/ui-test-junit4/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
index 8af350a..6977ddf 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
+++ b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
@@ -182,6 +182,10 @@
         }
     }
 
+    /**
+     * @param matcher
+     * @param useUnmergedTree
+     */
     override fun onNode(
         matcher: SemanticsMatcher,
         useUnmergedTree: Boolean
@@ -189,6 +193,10 @@
         return SemanticsNodeInteraction(testContext, useUnmergedTree, matcher)
     }
 
+    /**
+     * @param matcher
+     * @param useUnmergedTree
+     */
     override fun onAllNodes(
         matcher: SemanticsMatcher,
         useUnmergedTree: Boolean
diff --git a/compose/ui/ui-test/build.gradle b/compose/ui/ui-test/build.gradle
index 097486a..ead9fac 100644
--- a/compose/ui/ui-test/build.gradle
+++ b/compose/ui/ui-test/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,14 +23,115 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-text"))
+                api(project(":compose:ui:ui-unit"))
+                api(project(":compose:runtime:runtime"))
+                api(libs.kotlinStdlib)
+
+                implementation(project(":compose:ui:ui-util"))
+            }
+        }
+
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+                api(libs.kotlinCoroutinesCore)
+                api(libs.kotlinCoroutinesTest)
+                api(libs.kotlinStdlibCommon)
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api(project(":compose:ui:ui-graphics"))
+
+                implementation("androidx.annotation:annotation:1.1.0")
+                implementation("androidx.core:core-ktx:1.2.0")
+                implementation("androidx.test.espresso:espresso-core:3.5.0")
+                implementation(libs.testMonitor)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.junit)
+                    implementation(libs.truth)
+                    implementation(libs.skiko)
+                }
+            }
+        }
+
+        jvmTest {
+            dependencies {
+            }
+        }
+
+        androidCommonTest {
+            dependsOn(commonTest)
+            dependencies {
+                implementation(project(":compose:test-utils"))
+                implementation(libs.truth)
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependsOn(androidCommonTest)
+            dependencies {
+                implementation(project(":compose:material:material"))
+                implementation(project(":compose:ui:ui-test-junit4"))
+                implementation("androidx.activity:activity-compose:1.3.1")
+                implementation(libs.mockitoCore)
+                implementation(libs.mockitoKotlin)
+                implementation(libs.dexmakerMockito)
+                implementation(libs.kotlinTest)
+            }
+        }
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependsOn(androidCommonTest)
+            dependencies {
+                implementation(libs.mockitoCore4)
+                implementation(libs.mockitoKotlin4)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+            }
+        }
+    }
+}
 
 android {
-    if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        sourceSets {
-            test.java.srcDirs += "src/androidCommonTest/kotlin"
-            androidTest.java.srcDirs += "src/androidCommonTest/kotlin"
-        }
+    sourceSets {
+        test.java.srcDirs += "src/androidCommonTest/kotlin"
+        androidTest.java.srcDirs += "src/androidCommonTest/kotlin"
     }
 
     lintOptions {
@@ -41,121 +141,9 @@
 }
 
 dependencies {
-
-    if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api(project(":compose:ui:ui"))
-        api(project(":compose:ui:ui-graphics"))
-        api(project(":compose:ui:ui-text"))
-        api(project(":compose:ui:ui-unit"))
-        api(libs.kotlinCoroutinesCore)
-        api(libs.kotlinCoroutinesTest)
-        api(libs.kotlinStdlib)
-        api(libs.kotlinStdlibCommon)
-
-        implementation(project(":compose:ui:ui-util"))
-        implementation("androidx.annotation:annotation:1.1.0")
-        implementation("androidx.core:core-ktx:1.1.0")
-        implementation("androidx.test.espresso:espresso-core:3.5.0")
-        implementation("androidx.test:monitor:1.6.0")
-
-        testImplementation(project(":compose:test-utils"))
-        testImplementation(libs.truth)
-        testImplementation(libs.robolectric)
-        testImplementation(libs.mockitoCore4)
-        testImplementation(libs.mockitoKotlin4)
-
-        androidTestImplementation("androidx.activity:activity-compose:1.3.1")
-        androidTestImplementation(project(":compose:material:material"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoKotlin)
-        androidTestImplementation(libs.kotlinTest)
-
-        samples(project(":compose:ui:ui-test:ui-test-samples"))
-    }
-}
-
-
-if (AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        sourceSets {
-            commonMain.dependencies {
-                api(project(":compose:ui:ui"))
-                api(project(":compose:ui:ui-text"))
-                api(project(":compose:ui:ui-unit"))
-                api(libs.kotlinStdlib)
-
-                implementation(project(":compose:ui:ui-util"))
-            }
-
-            jvmMain.dependencies {
-                api(project(":compose:runtime:runtime"))
-                api(libs.kotlinCoroutinesCore)
-                api(libs.kotlinCoroutinesTest)
-                api(libs.kotlinStdlibCommon)
-            }
-
-            androidMain.dependencies {
-                api(project(":compose:ui:ui-graphics"))
-
-                implementation("androidx.annotation:annotation:1.1.0")
-                implementation("androidx.core:core-ktx:1.2.0")
-                implementation("androidx.test.espresso:espresso-core:3.3.0")
-                implementation(libs.testMonitor)
-            }
-
-            androidCommonTest.dependencies {
-                implementation(project(":compose:test-utils"))
-                implementation(libs.truth)
-            }
-
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.mockitoCore4)
-                implementation(libs.mockitoKotlin4)
-            }
-
-            androidAndroidTest.dependencies {
-                implementation(project(":compose:material:material"))
-                implementation(project(":compose:ui:ui-test-junit4"))
-                implementation("androidx.activity:activity-compose:1.3.1")
-                implementation(libs.mockitoCore)
-                implementation(libs.mockitoKotlin)
-                implementation(libs.dexmakerMockito)
-                implementation(libs.kotlinTest)
-            }
-
-            desktopMain.dependencies {
-                implementation(libs.junit)
-                implementation(libs.truth)
-                implementation(libs.skiko)
-            }
-
-            androidCommonTest.dependsOn(commonTest)
-            androidTest.dependsOn(androidCommonTest)
-            androidAndroidTest.dependsOn(androidCommonTest)
-        }
-    }
-
-    dependencies {
-        // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
-        // leaks into instrumented tests (b/214407011)
-        testImplementation(libs.robolectric)
-
-        samples(project(":compose:ui:ui-test:ui-test-samples"))
-    }
+    // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
+    // leaks into instrumented tests (b/214407011)
+    testImplementation(libs.robolectric)
 }
 
 androidx {
diff --git a/compose/ui/ui-test/samples/src/main/AndroidManifest.xml b/compose/ui/ui-test/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index a7cd314..0000000
--- a/compose/ui/ui-test/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/compose/ui/ui-test/src/androidMain/AndroidManifest.xml b/compose/ui/ui-test/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/compose/ui/ui-test/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-text-google-fonts/src/androidTest/AndroidManifest.xml b/compose/ui/ui-text-google-fonts/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e865f27..0000000
--- a/compose/ui/ui-text-google-fonts/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/compose/ui/ui-text-google-fonts/src/main/AndroidManifest.xml b/compose/ui/ui-text-google-fonts/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a69672..0000000
--- a/compose/ui/ui-text-google-fonts/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index c20c781..2903ae2 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,77 +23,15 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    dependencies {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        implementation(libs.kotlinStdlibCommon)
-        implementation(libs.kotlinCoroutinesCore)
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-        api(project(":compose:ui:ui-graphics"))
-        api(project(":compose:ui:ui-unit"))
-        api("androidx.annotation:annotation:1.1.0")
-
-        // when updating the runtime version please also update the runtime-saveable version
-        implementation("androidx.compose.runtime:runtime:1.2.1")
-        implementation("androidx.compose.runtime:runtime-saveable:1.2.1")
-
-        implementation(project(":compose:ui:ui-util"))
-        implementation(libs.kotlinStdlib)
-        implementation("androidx.core:core:1.7.0")
-        implementation('androidx.collection:collection:1.0.0')
-        implementation("androidx.emoji2:emoji2:1.2.0")
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.mockitoCore4)
-        testImplementation(libs.truth)
-        testImplementation(libs.kotlinReflect)
-        testImplementation(libs.kotlinTest)
-        testImplementation(libs.mockitoKotlin4)
-
-        androidTestImplementation(project(":internal-testutils-fonts"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
-        androidTestImplementation(libs.testCore)
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.espressoCore)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.mockitoKotlin)
-
-        samples(projectOrArtifact(":compose:ui:ui-text:ui-text-samples"))
-    }
-
-    android {
-        sourceSets {
-            main {
-                java.srcDirs += "${supportRootFolder}/text/text/src/main/java"
-            }
-        }
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
 
@@ -107,53 +44,58 @@
 
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            jvmMain.dependencies {
-                implementation(libs.kotlinStdlib)
+        commonTest {
+            dependencies {
             }
+        }
 
+        if (desktopEnabled) {
             skikoMain {
                 dependsOn(commonMain)
                 dependencies {
                     api(libs.skikoCommon)
+                    implementation(project(":compose:runtime:runtime"))
+                    implementation(project(":compose:runtime:runtime-saveable"))
                 }
             }
+        }
 
-            desktopMain {
-                dependsOn(skikoMain)
-                dependsOn(jvmMain)
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+                implementation(libs.kotlinStdlib)
             }
+        }
 
-            androidMain {
-                dependsOn(commonMain)
-            }
 
-            androidMain.dependencies {
+        androidMain {
+            dependsOn(commonMain)
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.annotation:annotation:1.1.0")
                 implementation("androidx.core:core:1.7.0")
                 implementation("androidx.emoji2:emoji2:1.2.0")
                 implementation('androidx.collection:collection:1.0.0')
             }
+        }
 
-            androidMain.kotlin.srcDirs("${supportRootFolder}/text/text/src/main/java")
-
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(project(":internal-testutils-fonts"))
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.mockitoCore4)
-                implementation(libs.truth)
-                implementation(libs.kotlinReflect)
-                implementation(libs.kotlinTest)
-                implementation(libs.mockitoKotlin4)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
             }
+        }
 
-            androidAndroidTest.dependencies {
+        jvmTest {
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation(project(":internal-testutils-fonts"))
                 implementation(libs.testRules)
@@ -165,20 +107,50 @@
                 implementation(libs.truth)
                 implementation(libs.mockitoKotlin)
             }
+        }
 
-            desktopTest.dependencies {
-                implementation(libs.truth)
+
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(project(":internal-testutils-fonts"))
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
                 implementation(libs.junit)
+                implementation(libs.truth)
+                implementation(libs.kotlinReflect)
                 implementation(libs.kotlinTest)
-                implementation(libs.skikoCurrentOs)
-                implementation(project(":compose:foundation:foundation"))
-                implementation(project(":compose:ui:ui-test-junit4"))
             }
         }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependencies {
+                    implementation(libs.truth)
+                    implementation(libs.junit)
+                    implementation(libs.kotlinTest)
+                    implementation(libs.skikoCurrentOs)
+                    implementation(project(":compose:foundation:foundation"))
+                    implementation(project(":compose:ui:ui-test-junit4"))
+                    implementation(project(":internal-testutils-fonts"))
+                }
+            }
+        }
+
+        androidMain.kotlin.srcDirs("${supportRootFolder}/text/text/src/main/java")
     }
-    dependencies {
-        samples(projectOrArtifact(":compose:ui:ui-text:ui-text-samples"))
-    }
+}
+
+dependencies {
+    // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
+    // leaks into instrumented tests (b/214407011)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
 }
 
 androidx {
diff --git a/compose/ui/ui-text/samples/src/main/AndroidManifest.xml b/compose/ui/ui-text/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a69672..0000000
--- a/compose/ui/ui-text/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/DrawTextSamples.kt b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/DrawTextSamples.kt
index 2a7f037..231274c 100644
--- a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/DrawTextSamples.kt
+++ b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/DrawTextSamples.kt
@@ -17,42 +17,116 @@
 package androidx.compose.ui.text.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.LocalTextStyle
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.CacheDrawScope
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.layout
-import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ParagraphStyle
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.drawText
+import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.rememberTextMeasurer
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.sp
+import kotlin.math.roundToInt
 
 @Sampled
 @Composable
-fun DrawTextLayoutResultSample() {
+fun DrawTextSample() {
+    val textMeasurer = rememberTextMeasurer()
+
+    Canvas(Modifier.fillMaxSize()) {
+        drawText(textMeasurer, "Hello, World!")
+    }
+}
+
+@Sampled
+@Composable
+fun DrawTextStyledSample() {
+    val textMeasurer = rememberTextMeasurer()
+
+    Canvas(Modifier.fillMaxSize()) {
+        drawText(
+            textMeasurer = textMeasurer,
+            text = "Hello, World!",
+            style = TextStyle(
+                fontSize = 24.sp,
+                fontWeight = FontWeight.Bold,
+                textDecoration = TextDecoration.Underline
+            )
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun DrawTextAnnotatedStringSample() {
+    val textMeasurer = rememberTextMeasurer()
+
+    Canvas(Modifier.fillMaxSize()) {
+        drawText(
+            textMeasurer = textMeasurer,
+            text = buildAnnotatedString {
+                withStyle(ParagraphStyle(textAlign = TextAlign.Start)) {
+                    append("Hello")
+                }
+                withStyle(ParagraphStyle(textAlign = TextAlign.End)) {
+                    append("World")
+                }
+            }
+        )
+    }
+}
+
+/**
+ * This sample demonstrates how to use layout phase to improve performance when drawing text on
+ * DrawScope. We can use [layout] Modifier or [Layout] composable to calculate the text layout
+ * during layout phase and then cache the result in a Snapshot aware state to draw it during draw
+ * phase.
+ */
+@Sampled
+@Composable
+fun DrawTextMeasureInLayoutSample() {
     val textMeasurer = rememberTextMeasurer()
     var textLayoutResult by remember {
         mutableStateOf<TextLayoutResult?>(null)
     }
 
     Canvas(
-        Modifier.fillMaxSize()
+        Modifier
+            .fillMaxSize()
             .layout { measurable, constraints ->
                 val placeable = measurable.measure(constraints)
                 // TextLayout can be done any time prior to its use in draw, including in a
                 // background thread.
-                // In this sample, text layout is done in compose layout. This way the layout call
-                // can be restarted when async font loading completes due to the fact that
+                // In this sample, text layout is measured in layout modifier. This way the layout
+                // call can be restarted when async font loading completes due to the fact that
                 // `.measure` call is executed in `.layout`.
                 textLayoutResult = textMeasurer.measure(
-                    text = AnnotatedString("Hello ".repeat(2)),
-                    style = TextStyle(fontSize = 35.sp)
+                    text = "Hello, World!",
+                    style = TextStyle(fontSize = 24.sp)
                 )
                 layout(placeable.width, placeable.height) {
                     placeable.placeRelative(0, 0)
@@ -62,3 +136,57 @@
         textLayoutResult?.let { drawText(it) }
     }
 }
+
+/**
+ * This sample demonstrates how to use [drawWithCache] modifier to improve performance when drawing
+ * text on DrawScope. We can use [drawWithCache] to calculate the text layout once in
+ * [CacheDrawScope] and then repeatedly use the same [TextLayoutResult] in the draw phase.
+ *
+ * This approach improves performance when the text itself does not change but its draw attributes
+ * do change over time, such as during a color animation.
+ */
+@Sampled
+@Composable
+fun DrawTextDrawWithCacheSample() {
+    // We can disable implicit caching since we will cache in DrawWithCache
+    val textMeasurer = rememberTextMeasurer(cacheSize = 0)
+    // Apply the current text style from theme, otherwise TextStyle.Default will be used.
+    val materialTextStyle = LocalTextStyle.current
+
+    // Animate color repeatedly
+    val infiniteTransition = rememberInfiniteTransition()
+    val color by infiniteTransition.animateColor(
+        initialValue = Color.Red,
+        targetValue = Color.Blue,
+        animationSpec = infiniteRepeatable(tween(1000))
+    )
+
+    Box(
+        Modifier
+            .fillMaxSize()
+            .drawWithCache {
+                // Text layout will be measured just once until the size of the drawing area or
+                // materialTextStyle changes.
+                val textLayoutResult = textMeasurer.measure(
+                    text = "Hello, World!",
+                    style = materialTextStyle,
+                    constraints = Constraints.fixed(
+                        width = (size.width / 2).roundToInt(),
+                        height = (size.height / 2).roundToInt()
+                    ),
+                    overflow = TextOverflow.Ellipsis
+                )
+                // color changes will only invalidate draw phase
+                onDrawWithContent {
+                    drawContent()
+                    drawText(
+                        textLayoutResult,
+                        color = color,
+                        topLeft = Offset(
+                            (size.width - textLayoutResult.size.width) / 2,
+                            (size.height - textLayoutResult.size.height) / 2,
+                        )
+                    )
+                }
+            })
+}
diff --git a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextMeasurerSamples.kt b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextMeasurerSamples.kt
new file mode 100644
index 0000000..9cbe75f
--- /dev/null
+++ b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextMeasurerSamples.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextMeasurer
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.sp
+
+/**
+ * Sample showing how to use TextMeasurer in a non-composable function with a plain String for text.
+ */
+@Sampled
+fun measureTextStringWithConstraints(textMeasurer: TextMeasurer) {
+    textMeasurer.measure(
+        text = "Hello, World",
+        style = TextStyle(
+            color = Color.Red,
+            fontSize = 16.sp,
+            fontFamily = FontFamily.Cursive
+        ),
+        constraints = Constraints(
+            minWidth = 400,
+            maxWidth = 400,
+            minHeight = 200,
+            maxHeight = 400
+        )
+    )
+}
+
+/**
+ * Sample showing how to use TextMeasurer in a non-composable function with an AnnotatedString
+ * for text.
+ */
+@Sampled
+fun measureTextAnnotatedString(textMeasurer: TextMeasurer) {
+    textMeasurer.measure(
+        text = buildAnnotatedString {
+            append("Hello, ")
+            withStyle(SpanStyle(color = Color.Blue)) {
+                append("World!")
+            }
+        },
+        style = TextStyle(
+            fontSize = 16.sp,
+            fontFamily = FontFamily.Monospace
+        )
+    )
+}
diff --git a/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextMotionSamples.kt b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextMotionSamples.kt
new file mode 100644
index 0000000..7fbf0de
--- /dev/null
+++ b/compose/ui/ui-text/samples/src/main/java/androidx/compose/ui/text/samples/TextMotionSamples.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.material.LocalTextStyle
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.text.style.TextMotion
+
+/**
+ * This sample demonstrates how to define TextMotion on a Text composable that scales up and down
+ * repeatedly.
+ */
+@Sampled
+@Composable
+fun TextMotionSample() {
+    val infiniteTransition = rememberInfiniteTransition()
+    val scale by infiniteTransition.animateFloat(
+        initialValue = 1f,
+        targetValue = 8f,
+        animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse)
+    )
+
+    Text(
+        text = "Hello",
+        modifier = Modifier
+            .graphicsLayer {
+                scaleX = scale
+                scaleY = scale
+                transformOrigin = TransformOrigin.Center
+            },
+        // Text composable does not take TextMotion as a parameter.
+        // Provide it via style argument but make sure that we are copying from current theme
+        style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated)
+    )
+}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/androidAndroidTest/AndroidManifest.xml b/compose/ui/ui-text/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/compose/ui/ui-text/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/ui/ui-text/src/androidMain/AndroidManifest.xml b/compose/ui/ui-text/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/compose/ui/ui-text/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
index 53effe0..61ac3a6 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
@@ -133,6 +133,8 @@
      * @param fontFamilyResolver to be used to load the font given in [SpanStyle]s. If not
      * specified, defaults to the value that was given during initialization of this [TextMeasurer].
      * @param skipCache Disables cache optimization if it is passed as true.
+     *
+     * @sample androidx.compose.ui.text.samples.measureTextAnnotatedString
      */
     @Stable
     fun measure(
@@ -221,6 +223,8 @@
      * @param fontFamilyResolver to be used to load the font given in [SpanStyle]s. If not
      * specified, defaults to the value that was given during initialization of this [TextMeasurer].
      * @param skipCache Disables cache optimization if it is passed as true.
+     *
+     * @sample androidx.compose.ui.text.samples.measureTextStringWithConstraints
      */
     @Stable
     fun measure(
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt
index f9aa838..23f39f9 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt
@@ -138,7 +138,7 @@
  * ([TextOverflow.Clip]) or becomes no-op.
  * @param blendMode Blending algorithm to be applied to the text
  *
- * @see TextMeasurer
+ * @sample androidx.compose.ui.text.samples.DrawTextAnnotatedStringSample
  */
 fun DrawScope.drawText(
     textMeasurer: TextMeasurer,
@@ -205,7 +205,8 @@
  * ([TextOverflow.Clip]) or becomes no-op.
  * @param blendMode Blending algorithm to be applied to the text
  *
- * @see TextMeasurer
+ * @sample androidx.compose.ui.text.samples.DrawTextSample
+ * @sample androidx.compose.ui.text.samples.DrawTextStyledSample
  */
 fun DrawScope.drawText(
     textMeasurer: TextMeasurer,
@@ -256,7 +257,8 @@
  * @param drawStyle Whether or not the text is stroked or filled in.
  * @param blendMode Blending algorithm to be applied to the text
  *
- * @sample androidx.compose.ui.text.samples.DrawTextLayoutResultSample
+ * @sample androidx.compose.ui.text.samples.DrawTextMeasureInLayoutSample
+ * @sample androidx.compose.ui.text.samples.DrawTextDrawWithCacheSample
  */
 fun DrawScope.drawText(
     textLayoutResult: TextLayoutResult,
@@ -317,8 +319,6 @@
  * @param textDecoration The decorations to paint on the text (e.g., an underline).
  * @param drawStyle Whether or not the text is stroked or filled in.
  * @param blendMode Blending algorithm to be applied to the text
- *
- * @sample androidx.compose.ui.text.samples.DrawTextLayoutResultSample
  */
 fun DrawScope.drawText(
     textLayoutResult: TextLayoutResult,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.kt
index 92a2f4c..c2f3ac6 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.kt
@@ -21,8 +21,8 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateMapOf
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
@@ -294,7 +294,7 @@
          * This is backed by a MutableState because it is incremented in [getOrCreateAdapter] which
          * can be called directly from a composition, inside a [remember] block.
          */
-        private var refCount by mutableStateOf(0)
+        private var refCount by mutableIntStateOf(0)
 
         val isRefCountZero get() = refCount == 0
 
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextMotion.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextMotion.kt
index 31381e8..29e7e35 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextMotion.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextMotion.kt
@@ -21,6 +21,8 @@
 
 /**
  * Defines ways to render and place glyphs to provide readability and smooth animations for text.
+ *
+ * @sample androidx.compose.ui.text.samples.TextMotionSample
  */
 @ExperimentalTextApi
 @Immutable
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index 13f1be5..b69203b 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -15,9 +15,8 @@
  */
 
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -25,79 +24,70 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    dependencies {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-
-        implementation(libs.kotlinStdlib)
-
-        api "androidx.annotation:annotation:1.1.0"
-
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api(project(":compose:ui:ui"))
-
-        androidTestImplementation project(":compose:ui:ui-test-junit4")
-
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.testCore)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testRules)
-
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(project(":compose:foundation:foundation-layout"))
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:material:material"))
-        androidTestImplementation("androidx.activity:activity-compose:1.3.1")
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
-
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlib)
 
-                api "androidx.annotation:annotation:1.1.0"
-
-                api("androidx.compose.runtime:runtime:1.2.1")
+                api(project(":compose:runtime:runtime"))
                 api(project(":compose:ui:ui"))
             }
-            jvmMain.dependencies {
-                implementation(libs.kotlinStdlib)
-            }
-            androidMain.dependencies {
-                api("androidx.annotation:annotation:1.1.0")
-            }
+        }
 
-            commonTest.dependencies {
+        commonTest {
+            dependencies {
                 implementation(kotlin("test-junit"))
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.truth)
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+                implementation(libs.kotlinStdlib)
             }
-            androidAndroidTest.dependencies {
+        }
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
+                api("androidx.annotation:annotation:1.1.0")
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
+
+                }
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:ui:ui-test-junit4"))
 
                 implementation(libs.junit)
@@ -112,9 +102,23 @@
                 implementation("androidx.activity:activity-compose:1.3.1")
             }
         }
-    }
-    dependencies {
-        samples(projectOrArtifact(":compose:ui:ui-unit:ui-unit-samples"))
+
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.truth)
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+
+                }
+            }
+        }
     }
 }
 
diff --git a/compose/ui/ui-tooling-data/src/androidMain/AndroidManifest.xml b/compose/ui/ui-tooling-data/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 0648ec7..0000000
--- a/compose/ui/ui-tooling-data/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
\ No newline at end of file
diff --git a/compose/ui/ui-tooling/build.gradle b/compose/ui/ui-tooling/build.gradle
index 507d5fcf..ea90aee 100644
--- a/compose/ui/ui-tooling/build.gradle
+++ b/compose/ui/ui-tooling/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -24,69 +23,51 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        implementation(libs.kotlinStdlib)
-
-        api("androidx.annotation:annotation:1.1.0")
-        implementation(project(":compose:animation:animation"))
-
-        api("androidx.compose.runtime:runtime:1.2.1")
-        api(project(":compose:ui:ui"))
-        api(project(":compose:ui:ui-tooling-preview"))
-        api(project(":compose:ui:ui-tooling-data"))
-        implementation("androidx.savedstate:savedstate-ktx:1.2.1")
-        implementation("androidx.compose.material:material:1.0.0")
-        implementation("androidx.activity:activity-compose:1.7.0")
-        implementation("androidx.lifecycle:lifecycle-common:2.6.1")
-
-        // kotlin-reflect and animation-tooling-internal are provided by Studio at runtime
-        compileOnly(project(":compose:animation:animation-tooling-internal"))
-        compileOnly(libs.kotlinReflect)
-
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
-
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(project(":compose:foundation:foundation-layout"))
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.kotlinReflect)
-        androidTestImplementation(project(":compose:animation:animation-tooling-internal"))
-        androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
-        androidTestImplementation(project(":compose:runtime:runtime-livedata"))
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 api(project(":compose:ui:ui-tooling-preview"))
                 api(project(":compose:runtime:runtime"))
                 api(project(":compose:ui:ui"))
                 api(project(":compose:ui:ui-tooling-data"))
             }
-            androidMain.dependencies {
+        }
+
+        commonTest {
+            dependencies {
+
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                    api(project(":compose:runtime:runtime"))
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.annotation:annotation:1.1.0")
                 implementation(project(":compose:animation:animation"))
                 implementation("androidx.savedstate:savedstate-ktx:1.2.1")
-                implementation(project(":compose:material:material"))
+                implementation("androidx.compose.material:material:1.0.0")
                 implementation("androidx.activity:activity-compose:1.7.0")
                 implementation("androidx.lifecycle:lifecycle-common:2.6.1")
 
@@ -94,14 +75,29 @@
                 compileOnly(project(":compose:animation:animation-tooling-internal"))
                 compileOnly(libs.kotlinReflect)
             }
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
-                implementation(project(":compose:runtime:runtime"))
-                implementation(project(":compose:ui:ui"))
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(skikoMain)
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+                    implementation(project(":compose:runtime:runtime"))
+                    implementation(project(":compose:ui:ui"))
+                }
             }
+        }
 
-            androidAndroidTest.dependencies {
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(project(":compose:ui:ui-test-junit4"))
 
                 implementation(libs.junit)
@@ -117,10 +113,25 @@
                 implementation(project(":compose:runtime:runtime-livedata"))
             }
         }
+
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+
+                }
+            }
+        }
     }
 }
 
-
 androidx {
     name = "Compose Tooling"
     type = LibraryType.PUBLISHED_LIBRARY
diff --git a/compose/ui/ui-unit/samples/src/main/AndroidManifest.xml b/compose/ui/ui-unit/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/ui/ui-unit/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/ui/ui-unit/src/androidMain/AndroidManifest.xml b/compose/ui/ui-unit/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index ee19b21..0000000
--- a/compose/ui/ui-unit/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-util/api/current.txt b/compose/ui/ui-util/api/current.txt
index 5e5e30e..a4ae8f0 100644
--- a/compose/ui/ui-util/api/current.txt
+++ b/compose/ui/ui-util/api/current.txt
@@ -21,6 +21,7 @@
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
     method public static inline <T> void fastForEachIndexed(java.util.List<? extends T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
     method public static inline <T> void fastForEachReversed(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
+    method public static inline <T> T? fastLastOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T, R> java.util.List<R> fastMap(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     method public static inline <T, R, C extends java.util.Collection<? super R>> C fastMapTo(java.util.List<? extends T>, C destination, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     method public static inline <T, R extends java.lang.Comparable<? super R>> T? fastMaxBy(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> selector);
diff --git a/compose/ui/ui-util/api/public_plus_experimental_current.txt b/compose/ui/ui-util/api/public_plus_experimental_current.txt
index 5e5e30e..a4ae8f0 100644
--- a/compose/ui/ui-util/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-util/api/public_plus_experimental_current.txt
@@ -21,6 +21,7 @@
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
     method public static inline <T> void fastForEachIndexed(java.util.List<? extends T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
     method public static inline <T> void fastForEachReversed(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
+    method public static inline <T> T? fastLastOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T, R> java.util.List<R> fastMap(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     method public static inline <T, R, C extends java.util.Collection<? super R>> C fastMapTo(java.util.List<? extends T>, C destination, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     method public static inline <T, R extends java.lang.Comparable<? super R>> T? fastMaxBy(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> selector);
diff --git a/compose/ui/ui-util/api/restricted_current.txt b/compose/ui/ui-util/api/restricted_current.txt
index 5e5e30e..a4ae8f0 100644
--- a/compose/ui/ui-util/api/restricted_current.txt
+++ b/compose/ui/ui-util/api/restricted_current.txt
@@ -21,6 +21,7 @@
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
     method public static inline <T> void fastForEachIndexed(java.util.List<? extends T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
     method public static inline <T> void fastForEachReversed(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
+    method public static inline <T> T? fastLastOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T, R> java.util.List<R> fastMap(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     method public static inline <T, R, C extends java.util.Collection<? super R>> C fastMapTo(java.util.List<? extends T>, C destination, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     method public static inline <T, R extends java.lang.Comparable<? super R>> T? fastMaxBy(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> selector);
diff --git a/compose/ui/ui-util/src/androidMain/AndroidManifest.xml b/compose/ui/ui-util/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index b47339d..0000000
--- a/compose/ui/ui-util/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt
index 25b8c3c..217cc13 100644
--- a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt
+++ b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt
@@ -203,3 +203,21 @@
     }
     return destination
 }
+
+/**
+ * Returns the last element matching the given [predicate], or `null` if no such element was found.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
+ */
+@Suppress("BanInlineOptIn")
+@OptIn(ExperimentalContracts::class)
+inline fun <T> List<T>.fastLastOrNull(predicate: (T) -> Boolean): T? {
+    contract { callsInPlace(predicate) }
+    for (index in indices.reversed()) {
+        val item = get(index)
+        if (predicate(item)) return item
+    }
+    return null
+}
diff --git a/compose/ui/ui-util/src/test/kotlin/androidx/compose/ui/util/ListUtilsTest.kt b/compose/ui/ui-util/src/test/kotlin/androidx/compose/ui/util/ListUtilsTest.kt
index ec25815..d59b8b1 100644
--- a/compose/ui/ui-util/src/test/kotlin/androidx/compose/ui/util/ListUtilsTest.kt
+++ b/compose/ui/ui-util/src/test/kotlin/androidx/compose/ui/util/ListUtilsTest.kt
@@ -115,4 +115,16 @@
         val list = listOf(0, -1, -500, 1)
         assertEquals(1, list.fastFirstOrNull { it > 0 })
     }
-}
\ No newline at end of file
+
+    @Test
+    fun lastOrNullFound() {
+        val list = listOf(1, -1, -500, 2)
+        assertEquals(2, list.fastLastOrNull { it > 0 })
+    }
+
+    @Test
+    fun lastOrNullNotFound() {
+        val list = listOf(-1, -1, -500, -2)
+        assertNull(list.fastLastOrNull { it > 0 })
+    }
+}
diff --git a/compose/ui/ui-viewbinding/samples/src/main/AndroidManifest.xml b/compose/ui/ui-viewbinding/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/compose/ui/ui-viewbinding/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/ui/ui-viewbinding/src/main/AndroidManifest.xml b/compose/ui/ui-viewbinding/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/compose/ui/ui-viewbinding/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index eeeb983..39aa37f 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -236,6 +236,7 @@
   }
 
   public final class DrawModifierKt {
+    method public static androidx.compose.ui.node.CacheDrawModifierNode CacheDrawModifierNode(kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
     method public static androidx.compose.ui.Modifier drawBehind(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
     method public static androidx.compose.ui.Modifier drawWithCache(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
     method public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
@@ -2264,6 +2265,10 @@
 
 package androidx.compose.ui.node {
 
+  public interface CacheDrawModifierNode extends androidx.compose.ui.node.DrawModifierNode {
+    method public void invalidateDrawCache();
+  }
+
   public interface CompositionLocalConsumerModifierNode extends androidx.compose.ui.node.DelegatableNode {
   }
 
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 6b29580..baaa2b1 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -318,6 +318,7 @@
   }
 
   public final class DrawModifierKt {
+    method public static androidx.compose.ui.node.CacheDrawModifierNode CacheDrawModifierNode(kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
     method public static androidx.compose.ui.Modifier drawBehind(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
     method public static androidx.compose.ui.Modifier drawWithCache(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
     method public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
@@ -2483,6 +2484,10 @@
 
 package androidx.compose.ui.node {
 
+  public interface CacheDrawModifierNode extends androidx.compose.ui.node.DrawModifierNode {
+    method public void invalidateDrawCache();
+  }
+
   public interface CompositionLocalConsumerModifierNode extends androidx.compose.ui.node.DelegatableNode {
   }
 
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index c17196f..7ce042d 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -236,6 +236,7 @@
   }
 
   public final class DrawModifierKt {
+    method public static androidx.compose.ui.node.CacheDrawModifierNode CacheDrawModifierNode(kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
     method public static androidx.compose.ui.Modifier drawBehind(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
     method public static androidx.compose.ui.Modifier drawWithCache(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
     method public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
@@ -2271,6 +2272,10 @@
 
 package androidx.compose.ui.node {
 
+  public interface CacheDrawModifierNode extends androidx.compose.ui.node.DrawModifierNode {
+    method public void invalidateDrawCache();
+  }
+
   @kotlin.PublishedApi internal interface ComposeUiNode {
     method public androidx.compose.runtime.CompositionLocalMap getCompositionLocalMap();
     method public androidx.compose.ui.unit.Density getDensity();
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 2b2f069..3beea81 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 import static androidx.inspection.gradle.InspectionPluginKt.packageInspector
 
@@ -26,130 +25,16 @@
     id("AndroidXComposePlugin")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-    constraints {
-        // In 1.4.0-alpha02 there was a change made in :compose:ui:ui which fixed an issue where
-        // we were over-invalidating layout. This change caused a corresponding regression in
-        // foundation's CoreText, where it was expecting a layout to happen but with this change
-        // it would not. A corresponding fix for this was added in 1.4.0-alpha02 of
-        // :compose:foundation:foundation. By adding this constraint, we are ensuring that the
-        // if an app has this ui module _and_ the foundation module as a dependency, then the
-        // version of foundation will be at least this version. This will prevent the bug in
-        // foundation from occurring. This does _NOT_ require that the app have foundation as
-        // a dependency.
-        implementation(project(":compose:foundation:foundation")) {
-            because 'prevents a critical bug in Text'
-        }
-    }
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        implementation(libs.kotlinStdlibCommon)
-        implementation(libs.kotlinCoroutinesCore)
 
-        // when updating the runtime version please also update the runtime-saveable version
-        implementation(project(":compose:runtime:runtime"))
-        api(project(":compose:runtime:runtime-saveable"))
-
-        api(project(":compose:ui:ui-geometry"))
-        api(project(":compose:ui:ui-graphics"))
-        api(project(":compose:ui:ui-text"))
-        api(project(":compose:ui:ui-unit"))
-        api("androidx.annotation:annotation:1.5.0")
-
-        // This has stub APIs for access to legacy Android APIs, so we don't want
-        // any dependency on this module.
-        compileOnly(project(":compose:ui:ui-android-stubs"))
-
-        implementation(project(":compose:ui:ui-util"))
-        implementation(libs.kotlinStdlib)
-        implementation("androidx.autofill:autofill:1.0.0")
-        implementation(libs.kotlinCoroutinesAndroid)
-
-        // Used to generate debug information in the layout inspector. If not present,
-        // we may fall back to more limited data.
-        compileOnly(libs.kotlinReflect)
-        testImplementation(libs.kotlinReflect)
-
-        implementation("androidx.activity:activity-ktx:1.7.0")
-        implementation(project(":core:core"))
-        implementation('androidx.collection:collection:1.0.0')
-        implementation("androidx.customview:customview-poolingcontainer:1.0.0")
-        implementation("androidx.savedstate:savedstate-ktx:1.2.1")
-        implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
-        implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
-        implementation("androidx.profileinstaller:profileinstaller:1.3.0")
-        implementation("androidx.emoji2:emoji2:1.2.0")
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.kotlinCoroutinesTest)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-        testImplementation(libs.mockitoCore4)
-        testImplementation(libs.mockitoKotlin4)
-        testImplementation(libs.robolectric)
-        testImplementation(project(":compose:ui:ui-test-junit4"))
-        testImplementation(project(":compose:test-utils"))
-
-        androidTestImplementation(libs.testCore)
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testExtJunitKtx)
-        androidTestImplementation(libs.testUiautomator)
-        androidTestImplementation(libs.kotlinCoroutinesTest)
-        androidTestImplementation(libs.kotlinTest)
-        androidTestImplementation(libs.espressoCore)
-        androidTestImplementation(libs.bundles.espressoContrib)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.mockitoKotlin)
-        androidTestImplementation(libs.material)
-        androidTestImplementation(project(":compose:animation:animation-core"))
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:foundation:foundation-layout"))
-        androidTestImplementation(project(":compose:material:material"))
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":internal-testutils-fonts"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
-        androidTestImplementation(project(":internal-testutils-runtime"))
-        androidTestImplementation(project(":test:screenshot:screenshot"))
-        androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
-        androidTestImplementation("androidx.recyclerview:recyclerview:1.3.0-alpha02")
-        androidTestImplementation("androidx.core:core-ktx:1.9.0")
-        androidTestImplementation("androidx.activity:activity-compose:1.7.0")
-        androidTestImplementation("androidx.appcompat:appcompat:1.3.0")
-        androidTestImplementation("androidx.fragment:fragment:1.3.0")
-
-        lintChecks(project(":compose:ui:ui-lint"))
-        lintPublish(project(":compose:ui:ui-lint"))
-
-        samples(project(":compose:ui:ui:ui-samples"))
-    }
-}
-
-packageInspector(project, ":compose:ui:ui-inspection")
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
 
@@ -163,8 +48,33 @@
                 api project(":compose:ui:ui-unit")
                 implementation(project(":compose:ui:ui-util"))
             }
+        }
 
-            androidMain.dependencies {
+        commonTest {
+            dependencies {
+                implementation(libs.kotlinReflect)
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            skikoMain {
+                dependsOn(commonMain)
+                dependencies {
+                    api(project(":compose:ui:ui-graphics"))
+                    api(libs.skikoCommon)
+                }
+            }
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 implementation(libs.kotlinStdlib)
                 // This has stub APIs for access to legacy Android APIs, so we don't want
                 // any dependency on this module.
@@ -181,48 +91,32 @@
                 implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
                 implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
                 implementation("androidx.emoji2:emoji2:1.2.0")
-            }
 
-            jvmMain.dependencies {
-                implementation(libs.kotlinStdlib)
+                implementation("androidx.profileinstaller:profileinstaller:1.3.0")
             }
-            skikoMain {
-                dependsOn(commonMain)
-                dependencies {
-                    api(project(":compose:ui:ui-graphics"))
-                    api(libs.skikoCommon)
-                }
-            }
+        }
+
+        if (desktopEnabled) {
             desktopMain {
                 dependsOn(skikoMain)
+                dependsOn(jvmMain)
                 dependencies {
+                    implementation(libs.kotlinStdlib)
                     implementation(libs.kotlinStdlibJdk8)
                     api(libs.kotlinCoroutinesSwing)
                 }
             }
+        }
 
-            commonTest.dependencies {
-                implementation(libs.kotlinReflect)
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
             }
+        }
 
-            // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
-            //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
-            //  level dependencies block instead:
-            //  `dependencies { testImplementation(libs.robolectric) }`
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.kotlinCoroutinesTest)
-                implementation(libs.junit)
-                implementation(libs.truth)
-                implementation(libs.mockitoCore4)
-                implementation(libs.mockitoKotlin4)
-                implementation(project(":compose:ui:ui-test-junit4"))
-                implementation(project(":internal-testutils-fonts"))
-                implementation(project(":compose:test-utils"))
-            }
-
-            androidAndroidTest.dependencies {
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation("androidx.fragment:fragment:1.3.0")
                 implementation("androidx.appcompat:appcompat:1.3.0")
                 implementation(libs.testUiautomator)
@@ -253,27 +147,75 @@
                 implementation("androidx.core:core-ktx:1.2.0")
                 implementation("androidx.activity:activity-compose:1.7.0")
             }
+        }
 
-            desktopTest.dependencies {
-                implementation(libs.truth)
+        // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+        //  need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+        //  level dependencies block instead:
+        //  `dependencies { testImplementation(libs.robolectric) }`
+        androidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.kotlinCoroutinesTest)
                 implementation(libs.junit)
-                implementation(libs.mockitoCore)
-                implementation(libs.mockitoKotlin)
-                implementation(libs.skikoCurrentOs)
-                implementation(project(":compose:material:material"))
+                implementation(libs.truth)
                 implementation(project(":compose:ui:ui-test-junit4"))
+                implementation(project(":internal-testutils-fonts"))
+                implementation(project(":compose:test-utils"))
+            }
+        }
+
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                    implementation(libs.truth)
+                    implementation(libs.junit)
+                    implementation(libs.mockitoCore)
+                    implementation(libs.mockitoKotlin)
+                    implementation(libs.skikoCurrentOs)
+                    implementation(project(":compose:material:material"))
+                    implementation(project(":compose:ui:ui-test-junit4"))
+                }
             }
         }
     }
-    dependencies {
-        samples(project(":compose:ui:ui:ui-samples"))
+}
 
-        // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
-        // leaks into instrumented tests (b/214407011)
-        testImplementation(libs.robolectric)
+dependencies {
+
+    constraints {
+        // In 1.4.0-alpha02 there was a change made in :compose:ui:ui which fixed an issue where
+        // we were over-invalidating layout. This change caused a corresponding regression in
+        // foundation's CoreText, where it was expecting a layout to happen but with this change
+        // it would not. A corresponding fix for this was added in 1.4.0-alpha02 of
+        // :compose:foundation:foundation. By adding this constraint, we are ensuring that the
+        // if an app has this ui module _and_ the foundation module as a dependency, then the
+        // version of foundation will be at least this version. This will prevent the bug in
+        // foundation from occurring. This does _NOT_ require that the app have foundation as
+        // a dependency.
+        implementation(project(":compose:foundation:foundation")) {
+            because 'prevents a critical bug in Text'
+        }
     }
 }
 
+packageInspector(project, ":compose:ui:ui-inspection")
+
+dependencies {
+    lintChecks(project(":compose:ui:ui-lint"))
+    lintPublish(project(":compose:ui:ui-lint"))
+
+    // Can't declare this in kotlin { sourceSets { androidTest.dependencies { .. } } } as that
+    // leaks into instrumented tests (b/214407011)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
+}
+
 androidx {
     name = "Compose UI primitives"
     type = LibraryType.PUBLISHED_LIBRARY
@@ -282,7 +224,7 @@
     legacyDisableKotlinStrictApiMode = true
 }
 
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
+if (desktopEnabled) {
     tasks.findByName("desktopTest").configure {
         systemProperties["GOLDEN_PATH"] = project.rootDir.absolutePath + "/../../golden"
     }
@@ -304,24 +246,3 @@
     // the androidx.compose.ui:ui-test library
     testNamespace "androidx.compose.ui.tests"
 }
-
-// Diagnostics for b/188565660
-def verifyKotlinModule(String variant) {
-    project.afterEvaluate {
-        def capitalVariant = variant.capitalize()
-        def moduleFile = new File("${buildDir}/tmp/kotlin-classes/${variant}/META-INF/ui_${variant}.kotlin_module")
-        tasks.named("compile${capitalVariant}Kotlin").configure { t ->
-            t.doLast {
-                // This file should be large, about 3.2K. If this file is short then many symbols will fail to resolve
-                if (moduleFile.length() < 250) {
-                    throw new GradleException("kotlin_module file ($moduleFile) too short! See b/188565660 for more information. File text: ${moduleFile.text}")
-                }
-            }
-        }
-    }
-}
-if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    for (variant in ["debug", "release"]) {
-        verifyKotlinModule(variant)
-    }
-}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/PopupDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/PopupDemo.kt
index 6e7516c..c656ad0 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/PopupDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/PopupDemo.kt
@@ -42,6 +42,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -59,7 +60,7 @@
 
 @Composable
 fun PopupDemo() {
-    val exampleIndex = remember { mutableStateOf(0) }
+    var exampleIndex by remember { mutableIntStateOf(0) }
     val totalExamples = 8
 
     Column {
@@ -71,11 +72,11 @@
                 text = "Prev",
                 color = Color.Cyan,
                 onClick = {
-                    if (exampleIndex.value == 0) {
-                        exampleIndex.value = totalExamples
+                    if (exampleIndex == 0) {
+                        exampleIndex = totalExamples
                     }
 
-                    exampleIndex.value = (exampleIndex.value - 1) % totalExamples
+                    exampleIndex = (exampleIndex - 1) % totalExamples
                 },
                 padding = 20.dp
             )
@@ -85,7 +86,7 @@
                 contentAlignment = Alignment.Center
             ) {
                 val description: String = {
-                    when (exampleIndex.value) {
+                    when (exampleIndex) {
                         0 -> "Shadow demo"
                         1 -> "Toggle a simple popup"
                         2 -> "Different content for the popup"
@@ -118,13 +119,13 @@
                 text = "Next",
                 color = Color.Cyan,
                 onClick = {
-                    exampleIndex.value = (exampleIndex.value + 1) % totalExamples
+                    exampleIndex = (exampleIndex + 1) % totalExamples
                 },
                 padding = 20.dp
             )
         }
 
-        when (exampleIndex.value) {
+        when (exampleIndex) {
             0 -> PopupElevation()
             1 -> PopupToggle()
             2 -> PopupWithChangingContent()
@@ -143,7 +144,7 @@
     var shape by remember { mutableStateOf(RectangleShape) }
     var background by remember { mutableStateOf(Color.Transparent) }
     var contentSize by remember { mutableStateOf(100.dp) }
-    var dismissCounter by remember { mutableStateOf(0) }
+    var dismissCounter by remember { mutableIntStateOf(0) }
     var elevation by remember { mutableStateOf(6.dp) }
 
     // This example utilizes the Card to draw its shadow.
@@ -238,18 +239,18 @@
     Column(Modifier.align(Alignment.CenterHorizontally)) {
         val heightSize = 120.dp
         val widthSize = 160.dp
-        val popupContentState = remember { mutableStateOf(0) }
+        var popupContentState by remember { mutableIntStateOf(0) }
         val totalContentExamples = 2
-        val popupCounter = remember { mutableStateOf(0) }
+        var popupCounter by remember { mutableIntStateOf(0) }
 
         Box(Modifier.size(widthSize, heightSize).background(Color.Gray)) {
             Popup(Alignment.Center) {
-                when (popupContentState.value % totalContentExamples) {
+                when (popupContentState % totalContentExamples) {
                     0 -> ClickableTextWithBackground(
-                        text = "Counter : ${popupCounter.value}",
+                        text = "Counter : $popupCounter",
                         color = Color.Green,
                         onClick = {
-                            popupCounter.value += 1
+                            popupCounter += 1
                         }
                     )
                     1 -> Box(
@@ -264,7 +265,7 @@
             text = "Change content",
             color = Color.Cyan,
             onClick = {
-                popupContentState.value += 1
+                popupContentState++
             }
         )
     }
@@ -328,7 +329,7 @@
     Column(Modifier.align(Alignment.CenterHorizontally)) {
         val heightSize = 200.dp
         val widthSize = 400.dp
-        val counter = remember { mutableStateOf(0) }
+        var counter by remember { mutableIntStateOf(0) }
         val popupAlignment = remember { mutableStateOf(Alignment.TopStart) }
         Box(
             modifier = Modifier.size(widthSize, heightSize).background(Color.Red),
@@ -339,8 +340,8 @@
                     text = "Click to change alignment",
                     color = Color.White,
                     onClick = {
-                        counter.value += 1
-                        when (counter.value % 9) {
+                        counter++
+                        when (counter % 9) {
                             0 -> popupAlignment.value = Alignment.TopStart
                             1 -> popupAlignment.value = Alignment.TopCenter
                             2 -> popupAlignment.value = Alignment.TopEnd
@@ -413,7 +414,7 @@
         val showPopup = remember { mutableStateOf(true) }
         val heightSize = 120.dp
         val widthSize = 160.dp
-        val rectangleState = remember { mutableStateOf(0) }
+        var rectangleState by remember { mutableIntStateOf(0) }
 
         Spacer(Modifier.height(15.dp))
         Box(
@@ -421,7 +422,7 @@
         ) {
             if (showPopup.value) {
                 Popup(Alignment.Center) {
-                    val size = when (rectangleState.value % 4) {
+                    val size = when (rectangleState % 4) {
                         0 -> Modifier.size(30.dp)
                         1 -> Modifier.size(100.dp)
                         2 -> Modifier.size(30.dp, 90.dp)
@@ -436,7 +437,7 @@
             text = "Change size",
             color = Color.Cyan,
             onClick = {
-                rectangleState.value += 1
+                rectangleState++
             }
         )
     }
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/HorizontalScrollersInVerticalScrollerDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/HorizontalScrollersInVerticalScrollerDemo.kt
index d11018e..e79abb8 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/HorizontalScrollersInVerticalScrollerDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/HorizontalScrollersInVerticalScrollerDemo.kt
@@ -34,6 +34,9 @@
 import androidx.compose.ui.draw.DrawModifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.clipRect
@@ -80,28 +83,28 @@
 @Composable
 private fun Scrollable(orientation: Orientation, content: @Composable () -> Unit) {
     val maxOffset = 0f
-    val offset = remember { mutableStateOf(maxOffset) }
-    val minOffset = remember { mutableStateOf(0f) }
+    var offset by remember { mutableFloatStateOf(maxOffset) }
+    var minOffset by remember { mutableFloatStateOf(0f) }
 
     Layout(
         content = content,
         modifier = Modifier.scrollable(
             orientation = orientation,
             state = rememberScrollableState { scrollDistance ->
-                val resultingOffset = offset.value + scrollDistance
+                val resultingOffset = offset + scrollDistance
                 val toConsume =
                     when {
                         resultingOffset > maxOffset -> {
-                            maxOffset - offset.value
+                            maxOffset - offset
                         }
-                        resultingOffset < minOffset.value -> {
-                            minOffset.value - offset.value
+                        resultingOffset < minOffset -> {
+                            minOffset - offset
                         }
                         else -> {
                             scrollDistance
                         }
                     }
-                offset.value = offset.value + toConsume
+                offset = offset + toConsume
                 toConsume
             }
         )
@@ -123,7 +126,7 @@
                     )
                 }
 
-            minOffset.value =
+            minOffset =
                 when (orientation) {
                     Orientation.Horizontal -> constraints.maxWidth.toFloat() - placeable.width
                     Orientation.Vertical -> constraints.maxHeight.toFloat() - placeable.height
@@ -143,8 +146,8 @@
 
             layout(width, height) {
                 when (orientation) {
-                    Orientation.Horizontal -> placeable.placeRelative(offset.value.roundToInt(), 0)
-                    Orientation.Vertical -> placeable.placeRelative(0, offset.value.roundToInt())
+                    Orientation.Horizontal -> placeable.placeRelative(offset.roundToInt(), 0)
+                    Orientation.Vertical -> placeable.placeRelative(0, offset.roundToInt())
                 }
             }
         }
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedScrollingDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedScrollingDemo.kt
index 3978b89..a6af47f 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedScrollingDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedScrollingDemo.kt
@@ -36,6 +36,9 @@
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.layout.Layout
@@ -79,8 +82,8 @@
  */
 @Composable
 private fun ScrollableContainer(content: @Composable () -> Unit) {
-    val offset = remember { mutableStateOf(0f) }
-    val maxOffset = remember { mutableStateOf(0f) }
+    var offset by remember { mutableFloatStateOf(0f) }
+    var maxOffset by remember { mutableFloatStateOf(0f) }
 
     Layout(
         content = content,
@@ -88,20 +91,20 @@
             .scrollable(
                 orientation = Orientation.Vertical,
                 state = rememberScrollableState { scrollDistance ->
-                    val resultingOffset = offset.value + scrollDistance
+                    val resultingOffset = offset + scrollDistance
                     val dyToConsume =
                         when {
                             resultingOffset > 0f -> {
-                                0f - offset.value
+                                0f - offset
                             }
-                            resultingOffset < maxOffset.value -> {
-                                maxOffset.value - offset.value
+                            resultingOffset < maxOffset -> {
+                                maxOffset - offset
                             }
                             else -> {
                                 scrollDistance
                             }
                         }
-                    offset.value += dyToConsume
+                    offset += dyToConsume
                     dyToConsume
                 }
             )
@@ -111,10 +114,10 @@
                 measurables.first()
                     .measure(constraints.copy(minHeight = 0, maxHeight = Constraints.Infinity))
 
-            maxOffset.value = (constraints.maxHeight - placeable.height).toFloat()
+            maxOffset = (constraints.maxHeight - placeable.height).toFloat()
 
             layout(constraints.maxWidth, constraints.maxHeight) {
-                placeable.placeRelative(0, offset.value.roundToInt())
+                placeable.placeRelative(0, offset.roundToInt())
             }
         }
     )
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt
index 0e7c5e7..17e91b3 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt
@@ -26,8 +26,10 @@
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
@@ -68,16 +70,16 @@
                 .background(color = Color.White)
         ) {
             items(100) {
-                val pointerCount = remember { mutableStateOf(0) }
+                var pointerCount by remember { mutableIntStateOf(0) }
 
                 Box(
                     Modifier.fillParentMaxSize()
                         .border(width = 1.dp, color = Color.Black)
-                        .pointerCounterGestureFilter { newCount -> pointerCount.value = newCount },
+                        .pointerCounterGestureFilter { newCount -> pointerCount = newCount },
                     contentAlignment = Alignment.Center
                 ) {
                     Text(
-                        "${pointerCount.value}",
+                        "$pointerCount",
                         fontSize = 16.em,
                         color = Color.Black
                     )
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
index fea7034..6d9fc2f 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
@@ -29,11 +29,13 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.Dp
@@ -70,10 +72,10 @@
 
     val interactionSource = remember { MutableInteractionSource() }
     val color = if (interactionSource.collectIsDraggedAsState().value) activeColor else idleColor
-    val offsetPx = remember { mutableStateOf(0f) }
+    var offsetPx by remember { mutableFloatStateOf(0f) }
 
     val offsetDp = with(LocalDensity.current) {
-        offsetPx.value.toDp()
+        offsetPx.toDp()
     }
     val (offsetX, offsetY) = when (orientation) {
         Orientation.Horizontal -> offsetDp to Dp.Hairline
@@ -88,7 +90,7 @@
                 interactionSource = interactionSource,
                 orientation = orientation,
                 state = rememberScrollableState { scrollDistance ->
-                    offsetPx.value += scrollDistance
+                    offsetPx += scrollDistance
                     scrollDistance
                 }
             )
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/TapPressureGestureDetectorDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/TapPressureGestureDetectorDemo.kt
index 0acd6aa91..709b169 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/TapPressureGestureDetectorDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/TapPressureGestureDetectorDemo.kt
@@ -30,6 +30,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -68,9 +69,9 @@
                 "For some pen supported devices, a finger touch pressure will equal 1.0."
         )
 
-        var gestureOffsetX by remember { mutableStateOf(0f) }
-        var gestureOffsetY by remember { mutableStateOf(0f) }
-        var gesturePressure by remember { mutableStateOf(0f) }
+        var gestureOffsetX by remember { mutableFloatStateOf(0f) }
+        var gestureOffsetY by remember { mutableFloatStateOf(0f) }
+        var gesturePressure by remember { mutableFloatStateOf(0f) }
 
         // Gestures (detectDragGestures) with pressure.
         Box(
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt
index 192a5b5..206cbf3 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt
@@ -41,6 +41,9 @@
 import androidx.compose.ui.draw.DrawModifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.clipRect
@@ -84,17 +87,16 @@
             -drawerWidth.toPx()
         }
 
-    val currentOffset = remember { mutableStateOf(minOffset) }
+    var currentOffset by remember { mutableFloatStateOf(minOffset) }
     val maxOffset = 0f
 
     Box(
         Modifier.scrollable(
             orientation = Orientation.Horizontal,
             state = rememberScrollableState { scrollDistance ->
-                val originalOffset = currentOffset.value
-                currentOffset.value =
-                    (currentOffset.value + scrollDistance).coerceIn(minOffset, maxOffset)
-                currentOffset.value - originalOffset
+                val originalOffset = currentOffset
+                currentOffset = (currentOffset + scrollDistance).coerceIn(minOffset, maxOffset)
+                currentOffset - originalOffset
             }
         )
     ) {
@@ -103,7 +105,7 @@
             Modifier
                 .fillMaxHeight()
                 .requiredWidth(drawerWidth)
-                .offset { IntOffset(currentOffset.value.roundToInt(), 0) }
+                .offset { IntOffset(currentOffset.roundToInt(), 0) }
                 .background(color = DefaultBackgroundColor)
         ) {
             Text(
@@ -122,28 +124,28 @@
 @Composable
 private fun Scrollable(orientation: Orientation, content: @Composable () -> Unit) {
     val maxOffset = 0f
-    val offset = remember { mutableStateOf(maxOffset) }
-    val minOffset = remember { mutableStateOf(0f) }
+    var offset by remember { mutableFloatStateOf(maxOffset) }
+    var minOffset by remember { mutableFloatStateOf(0f) }
 
     Layout(
         content = content,
         modifier = Modifier.scrollable(
             orientation = orientation,
             state = rememberScrollableState { scrollDistance ->
-                val resultingOffset = offset.value + scrollDistance
+                val resultingOffset = offset + scrollDistance
                 val toConsume =
                     when {
                         resultingOffset > maxOffset -> {
-                            maxOffset - offset.value
+                            maxOffset - offset
                         }
-                        resultingOffset < minOffset.value -> {
-                            minOffset.value - offset.value
+                        resultingOffset < minOffset -> {
+                            minOffset - offset
                         }
                         else -> {
                             scrollDistance
                         }
                     }
-                offset.value = offset.value + toConsume
+                offset += toConsume
                 toConsume
             }
         ).then(ClipModifier),
@@ -164,7 +166,7 @@
                     )
                 }
 
-            minOffset.value =
+            minOffset =
                 when (orientation) {
                     Orientation.Horizontal -> constraints.maxWidth.toFloat() - placeable.width
                     Orientation.Vertical -> constraints.maxHeight.toFloat() - placeable.height
@@ -184,8 +186,8 @@
 
             layout(width, height) {
                 when (orientation) {
-                    Orientation.Horizontal -> placeable.placeRelative(offset.value.roundToInt(), 0)
-                    Orientation.Vertical -> placeable.placeRelative(0, offset.value.roundToInt())
+                    Orientation.Horizontal -> placeable.placeRelative(offset.roundToInt(), 0)
+                    Orientation.Vertical -> placeable.placeRelative(0, offset.roundToInt())
                 }
             }
         }
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/recyclerview/RecyclerViewInteropDemoFragment.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/recyclerview/RecyclerViewInteropDemoFragment.kt
index 507e519..6a610255 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/recyclerview/RecyclerViewInteropDemoFragment.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/recyclerview/RecyclerViewInteropDemoFragment.kt
@@ -34,6 +34,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -122,7 +123,7 @@
     attrs: AttributeSet? = null,
     defStyle: Int = 0
 ) : AbstractComposeView(context, attrs, defStyle) {
-    var index by mutableStateOf(0)
+    var index by mutableIntStateOf(0)
     var rowState: LazyListState? by mutableStateOf(null)
 
     @Composable
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt
index 8ccc757..c584f2e 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt
@@ -44,8 +44,10 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.demos.R
@@ -73,14 +75,14 @@
 @Composable
 private fun OuterComposeWithNestedScroll(factory: (Context) -> View) {
     val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
-    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
+    var toolbarOffsetHeightPx by remember { mutableFloatStateOf(0f) }
 
     val nestedScrollConnection = remember {
         object : NestedScrollConnection {
             override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                 val delta = available.y
-                val newOffset = toolbarOffsetHeightPx.value + delta
-                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
+                val newOffset = toolbarOffsetHeightPx + delta
+                toolbarOffsetHeightPx = newOffset.coerceIn(-toolbarHeightPx, 0f)
                 return Offset.Zero
             }
         }
@@ -96,8 +98,8 @@
         TopAppBar(
             modifier = Modifier
                 .height(ToolbarHeight)
-                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
-            title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") }
+                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.roundToInt()) },
+            title = { Text("toolbar offset is $toolbarOffsetHeightPx") }
         )
 
         // Android View
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ScrollingAndroidViewsDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ScrollingAndroidViewsDemo.kt
index 20eb48b..6cfb3ae 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ScrollingAndroidViewsDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ScrollingAndroidViewsDemo.kt
@@ -30,6 +30,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -79,7 +80,7 @@
     checkedItems: Set<Int>,
     onChangeCheck: (Int, Boolean) -> Unit
 ) {
-    var allocationCounter by remember { mutableStateOf(0) }
+    var allocationCounter by remember { mutableIntStateOf(0) }
     val resetViews = remember { mutableSetOf<View>() }
 
     LazyColumn {
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
index 4300e08..20cfb51 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
@@ -30,7 +30,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -56,7 +56,7 @@
         AndroidView({ context -> TextView(context).apply { text = "This is a TextView" } })
 
         // Compose Android View and update its size based on state. The AndroidView takes modifiers.
-        var size by remember { mutableStateOf(20) }
+        var size by remember { mutableIntStateOf(20) }
         AndroidView(::View, Modifier.clickable { size += 20 }.background(Color.Blue)) { view ->
             view.layoutParams = ViewGroup.LayoutParams(size, size)
         }
@@ -74,7 +74,7 @@
         Button(onClick = { squareRef.value!!.size += 50 }) {
             Text("Increase size of Android view")
         }
-        val colorIndex = remember { mutableStateOf(0) }
+        val colorIndex = remember { mutableIntStateOf(0) }
         Button(
             onClick = {
                 colorIndex.value = (colorIndex.value + 1) % 4
diff --git a/compose/ui/ui/samples/src/main/AndroidManifest.xml b/compose/ui/ui/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a69672..0000000
--- a/compose/ui/ui/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/compose/ui/ui/src/androidMain/AndroidManifest.xml b/compose/ui/ui/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index b47339d..0000000
--- a/compose/ui/ui/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
index e583a5a..af1dbfc 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
@@ -280,7 +280,7 @@
             else -> PointerType.Unknown
         }
 
-        val historical = mutableListOf<HistoricalChange>()
+        val historical = ArrayList<HistoricalChange>(motionEvent.historySize)
         with(motionEvent) {
             repeat(historySize) { pos ->
                 val x = getHistoricalX(index, pos)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt
index 73a1193..e13bda9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt
@@ -21,7 +21,6 @@
 import android.view.MotionEvent.ACTION_SCROLL
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
 
 internal actual typealias NativePointerButtons = Int
 internal actual typealias NativePointerKeyboardModifiers = Int
@@ -92,22 +91,22 @@
         null -> PointerEvent(changes, null)
         this.motionEvent -> PointerEvent(changes, internalPointerEvent)
         else -> {
-            val map = mutableMapOf<PointerId, PointerInputChange>()
+            val map = LinkedHashMap<PointerId, PointerInputChange>(changes.size)
+            val pointerEventData = ArrayList<PointerInputEventData>(changes.size)
             changes.fastForEach { change ->
                 map[change.id] = change
-            }
-            val pointerEventData = changes.fastMap {
-                PointerInputEventData(
-                    it.id,
-                    it.uptimeMillis,
-                    it.position,
-                    it.position,
-                    it.pressed,
-                    it.pressure,
-                    it.type,
-                    this.internalPointerEvent?.issuesEnterExitEvent(it.id) == true
+                pointerEventData += PointerInputEventData(
+                    change.id,
+                    change.uptimeMillis,
+                    change.position,
+                    change.position,
+                    change.pressed,
+                    change.pressure,
+                    change.type,
+                    this.internalPointerEvent?.issuesEnterExitEvent(change.id) == true
                 )
             }
+
             val pointerInputEvent =
                 PointerInputEvent(motionEvent.eventTime, pointerEventData, motionEvent)
             val event = InternalPointerEvent(map, pointerInputEvent)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index da63070..087323c 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -138,6 +138,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.util.fastLastOrNull
 import androidx.compose.ui.util.trace
 import androidx.compose.ui.viewinterop.AndroidViewHolder
 import androidx.core.view.AccessibilityDelegateCompat
@@ -1375,7 +1376,7 @@
             // methods use semantics data, and because semantics coordinates are local to
             // this view, the pointer _position_, not _positionOnScreen_, is the offset that
             // needs to be cached.
-            pointerInputEvent.pointers.lastOrNull { it.down }?.position?.let {
+            pointerInputEvent.pointers.fastLastOrNull { it.down }?.position?.let {
                 lastDownPointerPosition = it
             }
 
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt
index 87b2aed..4d6135f 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt
@@ -28,7 +28,7 @@
 import androidx.compose.runtime.PausableMonotonicFrameClock
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.InternalComposeUiApi
@@ -437,5 +437,5 @@
 }
 
 private class MotionDurationScaleImpl : MotionDurationScale {
-    override var scaleFactor by mutableStateOf(1f)
+    override var scaleFactor by mutableFloatStateOf(1f)
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
index aff7952..165c0fc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
 import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.CacheDrawModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.ObserverModifierNode
@@ -139,12 +140,12 @@
 
 private data class DrawWithCacheElement(
     val onBuildDrawCache: CacheDrawScope.() -> DrawResult
-) : ModifierNodeElement<CacheDrawNode>() {
-    override fun create(): CacheDrawNode {
-        return CacheDrawNode(CacheDrawScope(), onBuildDrawCache)
+) : ModifierNodeElement<CacheDrawModifierNodeImpl>() {
+    override fun create(): CacheDrawModifierNodeImpl {
+        return CacheDrawModifierNodeImpl(CacheDrawScope(), onBuildDrawCache)
     }
 
-    override fun update(node: CacheDrawNode) {
+    override fun update(node: CacheDrawModifierNodeImpl) {
         node.block = onBuildDrawCache
     }
 
@@ -154,10 +155,16 @@
     }
 }
 
-private class CacheDrawNode(
+fun CacheDrawModifierNode(
+    onBuildDrawCache: CacheDrawScope.() -> DrawResult
+): CacheDrawModifierNode {
+    return CacheDrawModifierNodeImpl(CacheDrawScope(), onBuildDrawCache)
+}
+
+private class CacheDrawModifierNodeImpl(
     private val cacheDrawScope: CacheDrawScope,
     block: CacheDrawScope.() -> DrawResult
-) : Modifier.Node(), DrawModifierNode, ObserverModifierNode, BuildDrawCacheParams {
+) : Modifier.Node(), CacheDrawModifierNode, ObserverModifierNode, BuildDrawCacheParams {
 
     private var isCacheValid = false
     var block: CacheDrawScope.() -> DrawResult = block
@@ -182,7 +189,7 @@
         invalidateDrawCache()
     }
 
-    private fun invalidateDrawCache() {
+    override fun invalidateDrawCache() {
         isCacheValid = false
         cacheDrawScope.drawResult = null
         invalidateDraw()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
index 5fa03797..d838abf 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
@@ -45,21 +45,22 @@
     }
 
     private fun <T> MutableSet<T>.scheduleInvalidation(node: T) {
-        // We don't schedule a node if it is already scheduled during this composition.
-        if (contains(node)) return
-
-        add(node)
-
-        // If this is the first node scheduled for invalidation,
-        // we set up a listener that runs after onApplyChanges.
-        if (focusTargetNodes.size + focusEventNodes.size + focusPropertiesNodes.size == 1) {
-            onRequestApplyChangesListener.invoke(invalidateNodes)
+        if (add(node)) {
+            // If this is the first node scheduled for invalidation,
+            // we set up a listener that runs after onApplyChanges.
+            if (focusTargetNodes.size + focusEventNodes.size + focusPropertiesNodes.size == 1) {
+                onRequestApplyChangesListener.invoke(invalidateNodes)
+            }
         }
     }
 
     private val invalidateNodes: () -> Unit = {
         // Process all the invalidated FocusProperties nodes.
         focusPropertiesNodes.forEach {
+            // We don't need to invalidate a focus properties node if it was scheduled for
+            // invalidation earlier in the composition but was then removed.
+            if (!it.node.isAttached) return@forEach
+
             it.visitSelfAndChildren(Nodes.FocusTarget) { focusTarget ->
                 focusTargetNodes.add(focusTarget)
             }
@@ -131,8 +132,8 @@
         focusTargetNodes.clear()
         focusTargetsWithInvalidatedFocusEvents.clear()
 
-        check(focusPropertiesNodes.isEmpty())
-        check(focusEventNodes.isEmpty())
-        check(focusTargetNodes.isEmpty())
+         check(focusPropertiesNodes.isEmpty()) { "Unprocessed FocusProperties nodes" }
+         check(focusEventNodes.isEmpty()) { "Unprocessed FocusEvent nodes" }
+         check(focusTargetNodes.isEmpty()) { "Unprocessed FocusTarget nodes" }
     }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
index 96c06cc..eab488c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
@@ -497,7 +497,7 @@
     override fun resetPointerInputHandler() {
         val localJob = pointerInputJob
         if (localJob != null) {
-            localJob.cancel(CancellationException())
+            localJob.cancel()
             pointerInputJob = null
         }
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt
index d8b0359..4c6b373 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt
@@ -55,7 +55,7 @@
     // TODO(lmr): we might want to add some safety wheels to prevent this from being called
     //  while one of the chains is being diffed / updated. Although that might only be
     //  necessary for visiting subtree.
-    check(node.isAttached)
+    check(node.isAttached) { "visitAncestors called on an unattached node" }
     var node: Modifier.Node? = if (includeSelf) node else node.parent
     var layout: LayoutNode? = requireLayoutNode()
     while (layout != null) {
@@ -75,7 +75,7 @@
 
 @Suppress("unused")
 internal fun DelegatableNode.nearestAncestor(mask: Int): Modifier.Node? {
-    check(node.isAttached)
+    check(node.isAttached) { "nearestAncestor called on an unattached node" }
     var node: Modifier.Node? = node.parent
     var layout: LayoutNode? = requireLayoutNode()
     while (layout != null) {
@@ -97,7 +97,7 @@
 internal inline fun DelegatableNode.visitSubtree(mask: Int, block: (Modifier.Node) -> Unit) {
     // TODO(lmr): we might want to add some safety wheels to prevent this from being called
     //  while one of the chains is being diffed / updated.
-    check(node.isAttached)
+    check(node.isAttached) { "visitSubtree called on an unattached node" }
     var node: Modifier.Node? = node.child
     var layout: LayoutNode? = requireLayoutNode()
     // we use this bespoke data structure here specifically for traversing children. In the
@@ -130,7 +130,7 @@
 }
 
 internal inline fun DelegatableNode.visitChildren(mask: Int, block: (Modifier.Node) -> Unit) {
-    check(node.isAttached)
+    check(node.isAttached) { "visitChildren called on an unattached node" }
     val branches = mutableVectorOf<Modifier.Node>()
     val child = node.child
     if (child == null)
@@ -160,7 +160,7 @@
  * traversing below it
  */
 internal inline fun DelegatableNode.visitSubtreeIf(mask: Int, block: (Modifier.Node) -> Boolean) {
-    check(node.isAttached)
+    check(node.isAttached) { "visitSubtreeIf called on an unattached node" }
     val branches = mutableVectorOf<Modifier.Node>()
     val child = node.child
     if (child == null)
@@ -187,7 +187,7 @@
     mask: Int,
     block: (Modifier.Node) -> Unit
 ) {
-    check(node.isAttached)
+    check(node.isAttached) { "visitLocalDescendants called on an unattached node" }
     val self = node
     if (self.aggregateChildKindSet and mask == 0) return
     var next = self.child
@@ -203,7 +203,7 @@
     mask: Int,
     block: (Modifier.Node) -> Unit
 ) {
-    check(node.isAttached)
+    check(node.isAttached) { "visitLocalAncestors called on an unattached node" }
     var next = node.parent
     while (next != null) {
         if (next.kindSet and mask != 0) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawModifierNode.kt
index 99cd8b2..05fb28e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawModifierNode.kt
@@ -33,6 +33,15 @@
 }
 
 /**
+ * Expands on the [androidx.compose.ui.node.DrawModifierNode] by adding the ability to invalidate
+ * the draw cache for changes in things like shapes and bitmaps (see Modifier.border for a usage
+ * examples).
+ */
+interface CacheDrawModifierNode : DrawModifierNode {
+    fun invalidateDrawCache()
+}
+
+/**
  * Invalidates this modifier's draw layer, ensuring that a draw pass will
  * be run on the next frame.
  */
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
index 6519bf8..2a0b6e9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
@@ -226,21 +226,25 @@
 
     override val parentData: Any?
         get() {
-            var data: Any? = null
-            val thisNode = tail
+            // NOTE: If you make changes to this getter, please check the generated bytecode to
+            // ensure no extra allocation is made. See the note below.
             if (layoutNode.nodes.has(Nodes.ParentData)) {
-                with(layoutNode.density) {
-                    layoutNode.nodes.tailToHead { node ->
-                        if (node.isKind(Nodes.ParentData)) {
-                            node.dispatchForKind(Nodes.ParentData) {
-                                data = with(it) { modifyParentData(data) }
-                            }
+                val thisNode = tail
+                // NOTE: Keep this mutable variable scoped inside the if statement. When moved
+                // to the outer scope of get(), this causes the compiler to generate a
+                // Ref$ObjectRef instance on every call of this getter.
+                var data: Any? = null
+                layoutNode.nodes.tailToHead { node ->
+                    if (node.isKind(Nodes.ParentData)) {
+                        node.dispatchForKind(Nodes.ParentData) {
+                            data = with(it) { layoutNode.density.modifyParentData(data) }
                         }
-                        if (node === thisNode) return@tailToHead
                     }
+                    if (node === thisNode) return@tailToHead
                 }
+                return data
             }
-            return data
+            return null
         }
 
     final override val parentLayoutCoordinates: LayoutCoordinates?
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
index 4121bed..53be7dc 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
@@ -36,7 +36,7 @@
 import androidx.compose.material.icons.filled.ArrowBack
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -71,7 +71,7 @@
 @Preview
 @Composable
 fun ComposeConstraintLayoutDemos() {
-    var displayedDemoIndex by remember { mutableStateOf(-1) }
+    var displayedDemoIndex by remember { mutableIntStateOf(-1) }
     val maxIndex = AllComposeConstraintLayoutDemos.size - 1
     Column {
         Column {
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/CollapsibleToolbarDemo.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/CollapsibleToolbarDemo.kt
index 21dd10a..a1aa8cc 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/CollapsibleToolbarDemo.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/CollapsibleToolbarDemo.kt
@@ -34,7 +34,7 @@
 import androidx.compose.material.icons.filled.Face
 import androidx.compose.material.icons.filled.Menu
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -241,30 +241,30 @@
 
     val maxPx = with(LocalDensity.current) { big.roundToPx().toFloat() }
     val minPx = with(LocalDensity.current) { small.roundToPx().toFloat() }
-    val toolbarHeight = remember { mutableStateOf(maxPx) }
+    val toolbarHeight = remember { mutableFloatStateOf(maxPx) }
 
     val nestedScrollConnection = remember {
         object : NestedScrollConnection {
             override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-                val height = toolbarHeight.value
+                val height = toolbarHeight.floatValue
 
                 if (height + available.y > maxPx) {
-                    toolbarHeight.value = maxPx
+                    toolbarHeight.floatValue = maxPx
                     return Offset(0f, maxPx - height)
                 }
 
                 if (height + available.y < minPx) {
-                    toolbarHeight.value = minPx
+                    toolbarHeight.floatValue = minPx
                     return Offset(0f, minPx - height)
                 }
 
-                toolbarHeight.value += available.y
+                toolbarHeight.floatValue += available.y
                 return Offset(0f, available.y)
             }
         }
     }
 
-    val progress = 1 - (toolbarHeight.value - minPx) / (maxPx - minPx)
+    val progress = 1 - (toolbarHeight.floatValue - minPx) / (maxPx - minPx)
 
     Column {
         MotionLayout(
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ReactionSelectorDemo.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ReactionSelectorDemo.kt
index 5377ef1..ecd1404 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ReactionSelectorDemo.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ReactionSelectorDemo.kt
@@ -28,6 +28,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -47,7 +48,7 @@
 @Preview
 @Composable
 fun ReactionSelectorDemo() {
-    var selected by remember { mutableStateOf(3) }
+    var selected by remember { mutableIntStateOf(3) }
     val transitionName = remember { mutableStateOf("transition1") }
     val emojis = remember { "😀 🙂 🤨 😐 😒 😬".split(' ') }
     val emojiNames = remember {
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 8e90956..0000000
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
index 1896407..aa46490 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
@@ -41,6 +41,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableLongStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.remember
@@ -290,7 +291,7 @@
             content = { content() })
     } else {
         val needsUpdate = remember {
-            mutableStateOf(0L)
+            mutableLongStateOf(0L)
         }
 
         val contentTracker = remember { mutableStateOf(Unit, neverEqualPolicy()) }
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionCarousel.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionCarousel.kt
index 2656198..61b5811 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionCarousel.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionCarousel.kt
@@ -26,6 +26,8 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -168,9 +170,9 @@
 
     val provider = rememberStateOfItemsProvider(content)
 
-    var componentWidth by remember { mutableStateOf(1000f) }
+    var componentWidth by remember { mutableFloatStateOf(1000f) }
     val swipeableState = rememberCarouselSwipeableState(swipeStateStart)
-    var mprogress = (swipeableState.offset.value / componentWidth)
+    var mprogress = (swipeableState.offset.floatValue / componentWidth)
 
     var state by remember {
         mutableStateOf(
@@ -183,11 +185,11 @@
             )
         )
     }
-    var currentIndex = remember { mutableStateOf(0) }
+    var currentIndex by remember { mutableIntStateOf(0) }
 
-    val anchors = if (currentIndex.value == 0) {
+    val anchors = if (currentIndex == 0) {
         mapOf(0f to swipeStateStart, componentWidth to swipeStateForward)
-    } else if (currentIndex.value == provider.value.count() - 1) {
+    } else if (currentIndex == provider.value.count() - 1) {
         mapOf(-componentWidth to swipeStateBackward, 0f to swipeStateStart)
     } else {
         mapOf(
@@ -232,7 +234,7 @@
                 state.direction = MotionCarouselDirection.FORWARD
             }
         }
-        currentIndex.value = state.index
+        currentIndex = state.index
     }
 
     MotionLayout(motionScene = motionScene,
@@ -254,7 +256,7 @@
             }
     ) {
         for (i in 0 until numSlots) {
-            val idx = i + currentIndex.value - initialSlotIndex
+            val idx = i + currentIndex - initialSlotIndex
             val visible = idx in 0 until provider.value.count()
             ItemHolder(i, slotPrefix, showSlots) {
                 if (visible) {
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
index a003146..d1aff18 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
@@ -29,6 +29,8 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableLongStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.remember
@@ -411,18 +413,18 @@
     content: @Composable (MotionLayoutScope.() -> Unit)
 ) {
     val needsUpdate = remember {
-        mutableStateOf(0L)
+        mutableLongStateOf(0L)
     }
 
-    val transition = remember(motionScene, needsUpdate.value) {
+    val transition = remember(motionScene, needsUpdate.longValue) {
         motionScene.getTransitionInstance("default")
     }
 
-    val initialStart = remember(motionScene, needsUpdate.value) {
+    val initialStart = remember(motionScene, needsUpdate.longValue) {
         val startId = transition?.getStartConstraintSetId() ?: "start"
         motionScene.getConstraintSetInstance(startId)
     }
-    val initialEnd = remember(motionScene, needsUpdate.value) {
+    val initialEnd = remember(motionScene, needsUpdate.longValue) {
         val endId = transition?.getEndConstraintSetId() ?: "end"
         motionScene.getConstraintSetInstance(endId)
     }
@@ -557,8 +559,8 @@
     val motionProgress = createAndUpdateMotionProgress(progress = progress)
     val transitionImpl = (transition as? TransitionImpl) ?: TransitionImpl.EMPTY
     // TODO: Merge this snippet with UpdateWithForcedIfNoUserChange
-    val needsUpdate = remember { mutableStateOf(0L) }
-    needsUpdate.value // Read the value to allow recomposition from informationReceiver
+    val needsUpdate = remember { mutableLongStateOf(0L) }
+    needsUpdate.longValue // Read the value to allow recomposition from informationReceiver
     informationReceiver?.setUpdateFlag(needsUpdate)
 
     UpdateWithForcedIfNoUserChange(
@@ -1005,7 +1007,7 @@
 @Composable
 internal fun createAndUpdateMotionProgress(progress: Float): MotionProgress {
     val motionProgress = remember {
-        MotionProgress.fromMutableState(mutableStateOf(progress))
+        MotionProgress.fromMutableState(mutableFloatStateOf(progress))
     }
     val last = remember { Ref<Float>().apply { value = progress } }
     if (last.value != progress) {
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionRenderDebug.java b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionRenderDebug.java
deleted file mode 100644
index e259358..0000000
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionRenderDebug.java
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.constraintlayout.compose;
-
-import android.graphics.Canvas;
-import android.graphics.DashPathEffect;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-
-import androidx.annotation.NonNull;
-import androidx.constraintlayout.core.motion.Motion;
-import androidx.constraintlayout.core.motion.MotionPaths;
-
-import java.util.HashMap;
-
-class MotionRenderDebug {
-    public static final int DEBUG_SHOW_NONE = 0;
-    public static final int DEBUG_SHOW_PROGRESS = 1;
-    public static final int DEBUG_SHOW_PATH = 2;
-
-    static final int MAX_KEY_FRAMES = 50;
-    private static final int DEBUG_PATH_TICKS_PER_MS = 16;
-    float[] mPoints;
-    int[] mPathMode;
-    float[] mKeyFramePoints;
-    Path mPath;
-    Paint mPaint;
-    Paint mPaintKeyframes;
-    Paint mPaintGraph;
-    Paint mTextPaint;
-    Paint mFillPaint;
-    private float[] mRectangle;
-    final int mRedColor = 0xFFFFAA33;
-    final int mKeyframeColor = 0xffe0759a;
-    final int mGraphColor = 0xFF33AA00;
-    final int mShadowColor = 0x77000000;
-    final int mDiamondSize = 10;
-    DashPathEffect mDashPathEffect;
-    int mKeyFrameCount;
-    Rect mBounds = new Rect();
-    boolean mPresentationMode = false;
-    int mShadowTranslate = 1;
-
-    MotionRenderDebug(float textSize) {
-
-        mPaint = new Paint();
-        mPaint.setAntiAlias(true);
-        mPaint.setColor(mRedColor);
-        mPaint.setStrokeWidth(2);
-        mPaint.setStyle(Paint.Style.STROKE);
-
-        mPaintKeyframes = new Paint();
-        mPaintKeyframes.setAntiAlias(true);
-        mPaintKeyframes.setColor(mKeyframeColor);
-        mPaintKeyframes.setStrokeWidth(2);
-        mPaintKeyframes.setStyle(Paint.Style.STROKE);
-
-        mPaintGraph = new Paint();
-        mPaintGraph.setAntiAlias(true);
-        mPaintGraph.setColor(mGraphColor);
-        mPaintGraph.setStrokeWidth(2);
-        mPaintGraph.setStyle(Paint.Style.STROKE);
-
-        mTextPaint = new Paint();
-        mTextPaint.setAntiAlias(true);
-        mTextPaint.setColor(mGraphColor);
-        mTextPaint.setTextSize(textSize);
-        mRectangle = new float[8];
-        mFillPaint = new Paint();
-        mFillPaint.setAntiAlias(true);
-        mDashPathEffect = new DashPathEffect(new float[]{4, 8}, 0);
-        mPaintGraph.setPathEffect(mDashPathEffect);
-        mKeyFramePoints = new float[MAX_KEY_FRAMES * 2];
-        mPathMode = new int[MAX_KEY_FRAMES];
-
-        if (mPresentationMode) {
-            mPaint.setStrokeWidth(8);
-            mFillPaint.setStrokeWidth(8);
-            mPaintKeyframes.setStrokeWidth(8);
-            mShadowTranslate = 4;
-        }
-    }
-
-    public void draw(Canvas canvas,
-            HashMap<String, Motion> frameArrayList,
-            int duration, int debugPath,
-            int layoutWidth, int layoutHeight) {
-        if (frameArrayList == null || frameArrayList.size() == 0) {
-            return;
-        }
-        canvas.save();
-
-        for (Motion motionController : frameArrayList.values()) {
-            draw(canvas, motionController, duration, debugPath,
-                    layoutWidth, layoutHeight);
-        }
-        canvas.restore();
-    }
-
-    public void draw(Canvas canvas,
-            Motion motionController,
-            int duration, int debugPath,
-            int layoutWidth, int layoutHeight) {
-        int mode = motionController.getDrawPath();
-        if (debugPath > 0 && mode == Motion.DRAW_PATH_NONE) {
-            mode = Motion.DRAW_PATH_BASIC;
-        }
-        if (mode == Motion.DRAW_PATH_NONE) { // do not draw path
-            return;
-        }
-
-        mKeyFrameCount = motionController.buildKeyFrames(mKeyFramePoints, mPathMode, null);
-
-        if (mode >= Motion.DRAW_PATH_BASIC) {
-
-            int frames = duration / DEBUG_PATH_TICKS_PER_MS;
-            if (mPoints == null || mPoints.length != frames * 2) {
-                mPoints = new float[frames * 2];
-                mPath = new Path();
-            }
-
-            canvas.translate(mShadowTranslate, mShadowTranslate);
-
-            mPaint.setColor(mShadowColor);
-            mFillPaint.setColor(mShadowColor);
-            mPaintKeyframes.setColor(mShadowColor);
-            mPaintGraph.setColor(mShadowColor);
-            motionController.buildPath(mPoints, frames);
-            drawAll(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight);
-            mPaint.setColor(mRedColor);
-            mPaintKeyframes.setColor(mKeyframeColor);
-            mFillPaint.setColor(mKeyframeColor);
-            mPaintGraph.setColor(mGraphColor);
-
-            canvas.translate(-mShadowTranslate, -mShadowTranslate);
-            drawAll(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight);
-            if (mode == Motion.DRAW_PATH_RECTANGLE) {
-                drawRectangle(canvas, motionController);
-            }
-        }
-
-    }
-
-
-    public void drawAll(Canvas canvas, int mode, int keyFrames, Motion motionController,
-            int layoutWidth, int layoutHeight) {
-        if (mode == Motion.DRAW_PATH_AS_CONFIGURED) {
-            drawPathAsConfigured(canvas);
-        }
-        if (mode == Motion.DRAW_PATH_RELATIVE) {
-            drawPathRelative(canvas);
-        }
-        if (mode == Motion.DRAW_PATH_CARTESIAN) {
-            drawPathCartesian(canvas);
-        }
-        drawBasicPath(canvas);
-        drawTicks(canvas, mode, keyFrames, motionController, layoutWidth, layoutHeight);
-    }
-
-
-    /**
-     * Draws the paths of the given {@link Motion motionController}, forcing the drawing mode
-     * {@link Motion#DRAW_PATH_BASIC}.
-     *
-     * @param canvas Canvas instance used to draw on
-     * @param motionController Controller containing path information
-     * @param duration Defined in milliseconds, sets the amount of ticks used to draw the path
-     *                 based on {@link #DEBUG_PATH_TICKS_PER_MS}
-     * @param layoutWidth Width of the containing MotionLayout
-     * @param layoutHeight Height of the containing MotionLayout
-     * @param drawPath Whether to draw the path, paths are drawn using dashed lines
-     * @param drawTicks Whether to draw diamond shaped ticks that indicate KeyPositions along a path
-     */
-    void basicDraw(@NonNull Canvas canvas,
-            @NonNull Motion motionController,
-            int duration,
-            int layoutWidth,
-            int layoutHeight,
-            boolean drawPath,
-            boolean drawTicks) {
-        int mode = Motion.DRAW_PATH_BASIC;
-        mKeyFrameCount = motionController.buildKeyFrames(mKeyFramePoints, mPathMode, null);
-
-        int frames = duration / DEBUG_PATH_TICKS_PER_MS;
-        if (mPoints == null || mPoints.length != frames * 2) {
-            mPoints = new float[frames * 2];
-            mPath = new Path();
-        }
-
-        canvas.translate(mShadowTranslate, mShadowTranslate);
-
-        mPaint.setColor(mShadowColor);
-        mFillPaint.setColor(mShadowColor);
-        mPaintKeyframes.setColor(mShadowColor);
-        mPaintGraph.setColor(mShadowColor);
-        motionController.buildPath(mPoints, frames);
-        if (drawPath) {
-            drawBasicPath(canvas);
-        }
-        if (drawTicks) {
-            drawTicks(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight);
-        }
-
-        mPaint.setColor(mRedColor);
-        mPaintKeyframes.setColor(mKeyframeColor);
-        mFillPaint.setColor(mKeyframeColor);
-        mPaintGraph.setColor(mGraphColor);
-        canvas.translate(-mShadowTranslate, -mShadowTranslate);
-        if (drawPath) {
-            drawBasicPath(canvas);
-        }
-        if (drawTicks) {
-            drawTicks(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight);
-        }
-    }
-
-    private void drawBasicPath(Canvas canvas) {
-        canvas.drawLines(mPoints, mPaint);
-    }
-
-    private void drawTicks(Canvas canvas, int mode, int keyFrames, Motion motionController,
-            int layoutWidth, int layoutHeight) {
-        int viewWidth = 0;
-        int viewHeight = 0;
-        if (motionController.getView() != null) {
-            viewWidth = motionController.getView().getWidth();
-            viewHeight = motionController.getView().getHeight();
-        }
-        for (int i = 1; i < keyFrames - 1; i++) {
-            if (mode == Motion.DRAW_PATH_AS_CONFIGURED
-                    && mPathMode[i - 1] == Motion.DRAW_PATH_NONE) {
-                continue;
-
-            }
-            float x = mKeyFramePoints[i * 2];
-            float y = mKeyFramePoints[i * 2 + 1];
-            mPath.reset();
-            mPath.moveTo(x, y + mDiamondSize);
-            mPath.lineTo(x + mDiamondSize, y);
-            mPath.lineTo(x, y - mDiamondSize);
-            mPath.lineTo(x - mDiamondSize, y);
-            mPath.close();
-            float dx = 0; //framePoint.translationX
-            float dy = 0; //framePoint.translationY
-            if (mode == Motion.DRAW_PATH_AS_CONFIGURED) {
-
-                if (mPathMode[i - 1] == MotionPaths.PERPENDICULAR) {
-                    drawPathRelativeTicks(canvas, x - dx, y - dy);
-                } else if (mPathMode[i - 1] == MotionPaths.CARTESIAN) {
-                    drawPathCartesianTicks(canvas, x - dx, y - dy);
-                } else if (mPathMode[i - 1] == MotionPaths.SCREEN) {
-                    drawPathScreenTicks(canvas, x - dx, y - dy,
-                            viewWidth, viewHeight, layoutWidth, layoutHeight);
-                }
-
-                canvas.drawPath(mPath, mFillPaint);
-            }
-            if (mode == Motion.DRAW_PATH_RELATIVE) {
-                drawPathRelativeTicks(canvas, x - dx, y - dy);
-            }
-            if (mode == Motion.DRAW_PATH_CARTESIAN) {
-                drawPathCartesianTicks(canvas, x - dx, y - dy);
-            }
-            if (mode == Motion.DRAW_PATH_SCREEN) {
-                drawPathScreenTicks(canvas, x - dx, y - dy,
-                        viewWidth, viewHeight, layoutWidth, layoutHeight);
-            }
-            if (dx != 0 || dy != 0) {
-                drawTranslation(canvas, x - dx, y - dy, x, y);
-            } else {
-                canvas.drawPath(mPath, mFillPaint);
-            }
-        }
-        if (mPoints.length > 1) {
-            // Draw the starting and ending circle
-            canvas.drawCircle(mPoints[0], mPoints[1], 8, mPaintKeyframes);
-            canvas.drawCircle(mPoints[mPoints.length - 2],
-                    mPoints[mPoints.length - 1], 8, mPaintKeyframes);
-        }
-    }
-
-    private void drawTranslation(Canvas canvas, float x1, float y1, float x2, float y2) {
-        canvas.drawRect(x1, y1, x2, y2, mPaintGraph);
-        canvas.drawLine(x1, y1, x2, y2, mPaintGraph);
-    }
-
-    private void drawPathRelative(Canvas canvas) {
-        canvas.drawLine(mPoints[0], mPoints[1],
-                mPoints[mPoints.length - 2], mPoints[mPoints.length - 1], mPaintGraph);
-    }
-
-    private void drawPathAsConfigured(Canvas canvas) {
-        boolean path = false;
-        boolean cart = false;
-        for (int i = 0; i < mKeyFrameCount; i++) {
-            if (mPathMode[i] == MotionPaths.PERPENDICULAR) {
-                path = true;
-            }
-            if (mPathMode[i] == MotionPaths.CARTESIAN) {
-                cart = true;
-            }
-        }
-        if (path) {
-            drawPathRelative(canvas);
-        }
-        if (cart) {
-            drawPathCartesian(canvas);
-        }
-    }
-
-    private void drawPathRelativeTicks(Canvas canvas, float x, float y) {
-        float x1 = mPoints[0];
-        float y1 = mPoints[1];
-        float x2 = mPoints[mPoints.length - 2];
-        float y2 = mPoints[mPoints.length - 1];
-        float dist = (float) Math.hypot(x1 - x2, y1 - y2);
-        float t = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / (dist * dist);
-        float xp = x1 + t * (x2 - x1);
-        float yp = y1 + t * (y2 - y1);
-
-        Path path = new Path();
-        path.moveTo(x, y);
-        path.lineTo(xp, yp);
-        float len = (float) Math.hypot(xp - x, yp - y);
-        String text = "" + ((int) (100 * len / dist)) / 100.0f;
-        getTextBounds(text, mTextPaint);
-        float off = len / 2 - mBounds.width() / 2;
-        canvas.drawTextOnPath(text, path, off, -20, mTextPaint);
-        canvas.drawLine(x, y, xp, yp, mPaintGraph);
-    }
-
-    void getTextBounds(String text, Paint paint) {
-        paint.getTextBounds(text, 0, text.length(), mBounds);
-    }
-
-    private void drawPathCartesian(Canvas canvas) {
-        float x1 = mPoints[0];
-        float y1 = mPoints[1];
-        float x2 = mPoints[mPoints.length - 2];
-        float y2 = mPoints[mPoints.length - 1];
-
-        canvas.drawLine(Math.min(x1, x2), Math.max(y1, y2),
-                Math.max(x1, x2), Math.max(y1, y2), mPaintGraph);
-        canvas.drawLine(Math.min(x1, x2), Math.min(y1, y2),
-                Math.min(x1, x2), Math.max(y1, y2), mPaintGraph);
-    }
-
-    private void drawPathCartesianTicks(Canvas canvas, float x, float y) {
-        float x1 = mPoints[0];
-        float y1 = mPoints[1];
-        float x2 = mPoints[mPoints.length - 2];
-        float y2 = mPoints[mPoints.length - 1];
-        float minx = Math.min(x1, x2);
-        float maxy = Math.max(y1, y2);
-        float xgap = x - Math.min(x1, x2);
-        float ygap = Math.max(y1, y2) - y;
-        // Horizontal line
-        String text = "" + ((int) (0.5 + 100 * xgap / Math.abs(x2 - x1))) / 100.0f;
-        getTextBounds(text, mTextPaint);
-        float off = xgap / 2 - mBounds.width() / 2;
-        canvas.drawText(text, off + minx, y - 20, mTextPaint);
-        canvas.drawLine(x, y,
-                Math.min(x1, x2), y, mPaintGraph);
-
-        // Vertical line
-        text = "" + ((int) (0.5 + 100 * ygap / Math.abs(y2 - y1))) / 100.0f;
-        getTextBounds(text, mTextPaint);
-        off = ygap / 2 - mBounds.height() / 2;
-        canvas.drawText(text, x + 5, maxy - off, mTextPaint);
-        canvas.drawLine(x, y,
-                x, Math.max(y1, y2), mPaintGraph);
-    }
-
-    private void drawPathScreenTicks(Canvas canvas, float x, float y, int viewWidth, int viewHeight,
-            int layoutWidth, int layoutHeight) {
-        float x1 = 0;
-        float y1 = 0;
-        float x2 = 1;
-        float y2 = 1;
-        float minx = 0;
-        float maxy = 0;
-        float xgap = x;
-        float ygap = y;
-        // Horizontal line
-        String text = "" + ((int) (0.5 + 100 * (xgap - viewWidth / 2)
-                / (layoutWidth - viewWidth))) / 100.0f;
-        getTextBounds(text, mTextPaint);
-        float off = xgap / 2 - mBounds.width() / 2;
-        canvas.drawText(text, off + minx, y - 20, mTextPaint);
-        canvas.drawLine(x, y,
-                Math.min(x1, x2), y, mPaintGraph);
-
-        // Vertical line
-        text = "" + ((int) (0.5 + 100 * (ygap - viewHeight / 2)
-                / (layoutHeight - viewHeight))) / 100.0f;
-        getTextBounds(text, mTextPaint);
-        off = ygap / 2 - mBounds.height() / 2;
-        canvas.drawText(text, x + 5, maxy - off, mTextPaint);
-        canvas.drawLine(x, y,
-                x, Math.max(y1, y2), mPaintGraph);
-    }
-
-    private void drawRectangle(Canvas canvas, Motion motionController) {
-        mPath.reset();
-        int rectFrames = 50;
-        for (int i = 0; i <= rectFrames; i++) {
-            float p = i / (float) rectFrames;
-            motionController.buildRect(p, mRectangle, 0);
-            mPath.moveTo(mRectangle[0], mRectangle[1]);
-            mPath.lineTo(mRectangle[2], mRectangle[3]);
-            mPath.lineTo(mRectangle[4], mRectangle[5]);
-            mPath.lineTo(mRectangle[6], mRectangle[7]);
-            mPath.close();
-        }
-        mPaint.setColor(0x44000000);
-        canvas.translate(2, 2);
-        canvas.drawPath(mPath, mPaint);
-
-        canvas.translate(-2, -2);
-        mPaint.setColor(0xFFFF0000);
-        canvas.drawPath(mPath, mPaint);
-    }
-}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionRenderDebug.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionRenderDebug.kt
new file mode 100644
index 0000000..1477c32
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionRenderDebug.kt
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.constraintlayout.compose
+
+import android.graphics.Canvas
+import android.graphics.DashPathEffect
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import androidx.constraintlayout.core.motion.Motion
+import androidx.constraintlayout.core.motion.MotionPaths
+
+internal class MotionRenderDebug(textSize: Float) {
+    var mPoints: FloatArray? = null
+    var mPathMode: IntArray
+    var mKeyFramePoints: FloatArray
+    var mPath: Path? = null
+    var mPaint: Paint
+    var mPaintKeyframes: Paint
+    var mPaintGraph: Paint
+    var mTextPaint: Paint
+    var mFillPaint: Paint
+    private val mRectangle: FloatArray
+    val mRedColor = -0x55cd
+    val mKeyframeColor = -0x1f8a66
+    val mGraphColor = -0xcc5600
+    val mShadowColor = 0x77000000
+    val mDiamondSize = 10
+    var mDashPathEffect: DashPathEffect
+    var mKeyFrameCount = 0
+    var mBounds = Rect()
+    var mPresentationMode = false
+    var mShadowTranslate = 1
+
+    init {
+        mPaint = Paint()
+        mPaint.isAntiAlias = true
+        mPaint.color = mRedColor
+        mPaint.strokeWidth = 2f
+        mPaint.style = Paint.Style.STROKE
+        mPaintKeyframes = Paint()
+        mPaintKeyframes.isAntiAlias = true
+        mPaintKeyframes.color = mKeyframeColor
+        mPaintKeyframes.strokeWidth = 2f
+        mPaintKeyframes.style = Paint.Style.STROKE
+        mPaintGraph = Paint()
+        mPaintGraph.isAntiAlias = true
+        mPaintGraph.color = mGraphColor
+        mPaintGraph.strokeWidth = 2f
+        mPaintGraph.style = Paint.Style.STROKE
+        mTextPaint = Paint()
+        mTextPaint.isAntiAlias = true
+        mTextPaint.color = mGraphColor
+        mTextPaint.textSize = textSize
+        mRectangle = FloatArray(8)
+        mFillPaint = Paint()
+        mFillPaint.isAntiAlias = true
+        mDashPathEffect = DashPathEffect(floatArrayOf(4f, 8f), 0f)
+        mPaintGraph.pathEffect = mDashPathEffect
+        mKeyFramePoints = FloatArray(MAX_KEY_FRAMES * 2)
+        mPathMode = IntArray(MAX_KEY_FRAMES)
+        if (mPresentationMode) {
+            mPaint.strokeWidth = 8f
+            mFillPaint.strokeWidth = 8f
+            mPaintKeyframes.strokeWidth = 8f
+            mShadowTranslate = 4
+        }
+    }
+
+    fun draw(
+        canvas: Canvas,
+        frameArrayList: HashMap<String?, Motion>?,
+        duration: Int,
+        debugPath: Int,
+        layoutWidth: Int,
+        layoutHeight: Int
+    ) {
+        if (frameArrayList == null || frameArrayList.size == 0) {
+            return
+        }
+        canvas.save()
+        for (motionController in frameArrayList.values) {
+            draw(
+                canvas, motionController, duration, debugPath,
+                layoutWidth, layoutHeight
+            )
+        }
+        canvas.restore()
+    }
+
+    fun draw(
+        canvas: Canvas,
+        motionController: Motion,
+        duration: Int,
+        debugPath: Int,
+        layoutWidth: Int,
+        layoutHeight: Int
+    ) {
+        var mode = motionController.drawPath
+        if (debugPath > 0 && mode == Motion.DRAW_PATH_NONE) {
+            mode = Motion.DRAW_PATH_BASIC
+        }
+        if (mode == Motion.DRAW_PATH_NONE) { // do not draw path
+            return
+        }
+        mKeyFrameCount = motionController.buildKeyFrames(mKeyFramePoints, mPathMode, null)
+        if (mode >= Motion.DRAW_PATH_BASIC) {
+            val frames = duration / DEBUG_PATH_TICKS_PER_MS
+            if (mPoints == null || mPoints!!.size != frames * 2) {
+                mPoints = FloatArray(frames * 2)
+                mPath = Path()
+            }
+            canvas.translate(mShadowTranslate.toFloat(), mShadowTranslate.toFloat())
+            mPaint.color = mShadowColor
+            mFillPaint.color = mShadowColor
+            mPaintKeyframes.color = mShadowColor
+            mPaintGraph.color = mShadowColor
+            motionController.buildPath(mPoints, frames)
+            drawAll(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight)
+            mPaint.color = mRedColor
+            mPaintKeyframes.color = mKeyframeColor
+            mFillPaint.color = mKeyframeColor
+            mPaintGraph.color = mGraphColor
+            canvas.translate(-mShadowTranslate.toFloat(), -mShadowTranslate.toFloat())
+            drawAll(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight)
+            if (mode == Motion.DRAW_PATH_RECTANGLE) {
+                drawRectangle(canvas, motionController)
+            }
+        }
+    }
+
+    fun drawAll(
+        canvas: Canvas,
+        mode: Int,
+        keyFrames: Int,
+        motionController: Motion,
+        layoutWidth: Int,
+        layoutHeight: Int
+    ) {
+        if (mode == Motion.DRAW_PATH_AS_CONFIGURED) {
+            drawPathAsConfigured(canvas)
+        }
+        if (mode == Motion.DRAW_PATH_RELATIVE) {
+            drawPathRelative(canvas)
+        }
+        if (mode == Motion.DRAW_PATH_CARTESIAN) {
+            drawPathCartesian(canvas)
+        }
+        drawBasicPath(canvas)
+        drawTicks(canvas, mode, keyFrames, motionController, layoutWidth, layoutHeight)
+    }
+
+    /**
+     * Draws the paths of the given [motionController][Motion], forcing the drawing mode
+     * [Motion.DRAW_PATH_BASIC].
+     *
+     * @param canvas Canvas instance used to draw on
+     * @param motionController Controller containing path information
+     * @param duration Defined in milliseconds, sets the amount of ticks used to draw the path
+     * based on [.DEBUG_PATH_TICKS_PER_MS]
+     * @param layoutWidth Width of the containing MotionLayout
+     * @param layoutHeight Height of the containing MotionLayout
+     * @param drawPath Whether to draw the path, paths are drawn using dashed lines
+     * @param drawTicks Whether to draw diamond shaped ticks that indicate KeyPositions along a path
+     */
+    fun basicDraw(
+        canvas: Canvas,
+        motionController: Motion,
+        duration: Int,
+        layoutWidth: Int,
+        layoutHeight: Int,
+        drawPath: Boolean,
+        drawTicks: Boolean
+    ) {
+        val mode = Motion.DRAW_PATH_BASIC
+        mKeyFrameCount = motionController.buildKeyFrames(mKeyFramePoints, mPathMode, null)
+        val frames = duration / DEBUG_PATH_TICKS_PER_MS
+        if (mPoints == null || mPoints!!.size != frames * 2) {
+            mPoints = FloatArray(frames * 2)
+            mPath = Path()
+        }
+        canvas.translate(mShadowTranslate.toFloat(), mShadowTranslate.toFloat())
+        mPaint.color = mShadowColor
+        mFillPaint.color = mShadowColor
+        mPaintKeyframes.color = mShadowColor
+        mPaintGraph.color = mShadowColor
+        motionController.buildPath(mPoints, frames)
+        if (drawPath) {
+            drawBasicPath(canvas)
+        }
+        if (drawTicks) {
+            drawTicks(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight)
+        }
+        mPaint.color = mRedColor
+        mPaintKeyframes.color = mKeyframeColor
+        mFillPaint.color = mKeyframeColor
+        mPaintGraph.color = mGraphColor
+        canvas.translate(-mShadowTranslate.toFloat(), -mShadowTranslate.toFloat())
+        if (drawPath) {
+            drawBasicPath(canvas)
+        }
+        if (drawTicks) {
+            drawTicks(canvas, mode, mKeyFrameCount, motionController, layoutWidth, layoutHeight)
+        }
+    }
+
+    private fun drawBasicPath(canvas: Canvas) {
+        canvas.drawLines(mPoints!!, mPaint)
+    }
+
+    private fun drawTicks(
+        canvas: Canvas,
+        mode: Int,
+        keyFrames: Int,
+        motionController: Motion,
+        layoutWidth: Int,
+        layoutHeight: Int
+    ) {
+        var viewWidth = 0
+        var viewHeight = 0
+        if (motionController.view != null) {
+            viewWidth = motionController.view.width
+            viewHeight = motionController.view.height
+        }
+        for (i in 1 until keyFrames - 1) {
+            if (
+                mode == Motion.DRAW_PATH_AS_CONFIGURED && mPathMode[i - 1] == Motion.DRAW_PATH_NONE
+            ) {
+                continue
+            }
+            val x = mKeyFramePoints[i * 2]
+            val y = mKeyFramePoints[i * 2 + 1]
+            mPath!!.reset()
+            mPath!!.moveTo(x, y + mDiamondSize)
+            mPath!!.lineTo(x + mDiamondSize, y)
+            mPath!!.lineTo(x, y - mDiamondSize)
+            mPath!!.lineTo(x - mDiamondSize, y)
+            mPath!!.close()
+            val dx = 0f // framePoint.translationX
+            val dy = 0f // framePoint.translationY
+            if (mode == Motion.DRAW_PATH_AS_CONFIGURED) {
+                if (mPathMode[i - 1] == MotionPaths.PERPENDICULAR) {
+                    drawPathRelativeTicks(canvas, x - dx, y - dy)
+                } else if (mPathMode[i - 1] == MotionPaths.CARTESIAN) {
+                    drawPathCartesianTicks(canvas, x - dx, y - dy)
+                } else if (mPathMode[i - 1] == MotionPaths.SCREEN) {
+                    drawPathScreenTicks(
+                        canvas, x - dx, y - dy,
+                        viewWidth, viewHeight, layoutWidth, layoutHeight
+                    )
+                }
+                canvas.drawPath(mPath!!, mFillPaint)
+            }
+            if (mode == Motion.DRAW_PATH_RELATIVE) {
+                drawPathRelativeTicks(canvas, x - dx, y - dy)
+            }
+            if (mode == Motion.DRAW_PATH_CARTESIAN) {
+                drawPathCartesianTicks(canvas, x - dx, y - dy)
+            }
+            if (mode == Motion.DRAW_PATH_SCREEN) {
+                drawPathScreenTicks(
+                    canvas, x - dx, y - dy,
+                    viewWidth, viewHeight, layoutWidth, layoutHeight
+                )
+            }
+            if (dx != 0f || dy != 0f) {
+                drawTranslation(canvas, x - dx, y - dy, x, y)
+            } else {
+                canvas.drawPath(mPath!!, mFillPaint)
+            }
+        }
+        if (mPoints!!.size > 1) {
+            // Draw the starting and ending circle
+            canvas.drawCircle(mPoints!![0], mPoints!![1], 8f, mPaintKeyframes)
+            canvas.drawCircle(
+                mPoints!![mPoints!!.size - 2],
+                mPoints!![mPoints!!.size - 1], 8f, mPaintKeyframes
+            )
+        }
+    }
+
+    private fun drawTranslation(canvas: Canvas, x1: Float, y1: Float, x2: Float, y2: Float) {
+        canvas.drawRect(x1, y1, x2, y2, mPaintGraph)
+        canvas.drawLine(x1, y1, x2, y2, mPaintGraph)
+    }
+
+    private fun drawPathRelative(canvas: Canvas) {
+        canvas.drawLine(
+            mPoints!![0], mPoints!![1],
+            mPoints!![mPoints!!.size - 2], mPoints!![mPoints!!.size - 1], mPaintGraph
+        )
+    }
+
+    private fun drawPathAsConfigured(canvas: Canvas) {
+        var path = false
+        var cart = false
+        for (i in 0 until mKeyFrameCount) {
+            if (mPathMode[i] == MotionPaths.PERPENDICULAR) {
+                path = true
+            }
+            if (mPathMode[i] == MotionPaths.CARTESIAN) {
+                cart = true
+            }
+        }
+        if (path) {
+            drawPathRelative(canvas)
+        }
+        if (cart) {
+            drawPathCartesian(canvas)
+        }
+    }
+
+    private fun drawPathRelativeTicks(canvas: Canvas, x: Float, y: Float) {
+        val x1 = mPoints!![0]
+        val y1 = mPoints!![1]
+        val x2 = mPoints!![mPoints!!.size - 2]
+        val y2 = mPoints!![mPoints!!.size - 1]
+        val dist = Math.hypot((x1 - x2).toDouble(), (y1 - y2).toDouble()).toFloat()
+        val t = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / (dist * dist)
+        val xp = x1 + t * (x2 - x1)
+        val yp = y1 + t * (y2 - y1)
+        val path = Path()
+        path.moveTo(x, y)
+        path.lineTo(xp, yp)
+        val len = Math.hypot((xp - x).toDouble(), (yp - y).toDouble()).toFloat()
+        val text = "" + (100 * len / dist).toInt() / 100.0f
+        getTextBounds(text, mTextPaint)
+        val off = len / 2 - mBounds.width() / 2
+        canvas.drawTextOnPath(text, path, off, -20f, mTextPaint)
+        canvas.drawLine(x, y, xp, yp, mPaintGraph)
+    }
+
+    fun getTextBounds(text: String, paint: Paint) {
+        paint.getTextBounds(text, 0, text.length, mBounds)
+    }
+
+    private fun drawPathCartesian(canvas: Canvas) {
+        val x1 = mPoints!![0]
+        val y1 = mPoints!![1]
+        val x2 = mPoints!![mPoints!!.size - 2]
+        val y2 = mPoints!![mPoints!!.size - 1]
+        canvas.drawLine(
+            Math.min(x1, x2), Math.max(y1, y2),
+            Math.max(x1, x2), Math.max(y1, y2), mPaintGraph
+        )
+        canvas.drawLine(
+            Math.min(x1, x2), Math.min(y1, y2),
+            Math.min(x1, x2), Math.max(y1, y2), mPaintGraph
+        )
+    }
+
+    private fun drawPathCartesianTicks(canvas: Canvas, x: Float, y: Float) {
+        val x1 = mPoints!![0]
+        val y1 = mPoints!![1]
+        val x2 = mPoints!![mPoints!!.size - 2]
+        val y2 = mPoints!![mPoints!!.size - 1]
+        val minx = Math.min(x1, x2)
+        val maxy = Math.max(y1, y2)
+        val xgap = x - Math.min(x1, x2)
+        val ygap = Math.max(y1, y2) - y
+        // Horizontal line
+        var text = "" + (0.5 + 100 * xgap / Math.abs(x2 - x1)).toInt() / 100.0f
+        getTextBounds(text, mTextPaint)
+        var off = xgap / 2 - mBounds.width() / 2
+        canvas.drawText(text, off + minx, y - 20, mTextPaint)
+        canvas.drawLine(
+            x, y,
+            Math.min(x1, x2), y, mPaintGraph
+        )
+
+        // Vertical line
+        text = "" + (0.5 + 100 * ygap / Math.abs(y2 - y1)).toInt() / 100.0f
+        getTextBounds(text, mTextPaint)
+        off = ygap / 2 - mBounds.height() / 2
+        canvas.drawText(text, x + 5, maxy - off, mTextPaint)
+        canvas.drawLine(
+            x, y,
+            x, Math.max(y1, y2), mPaintGraph
+        )
+    }
+
+    private fun drawPathScreenTicks(
+        canvas: Canvas,
+        x: Float,
+        y: Float,
+        viewWidth: Int,
+        viewHeight: Int,
+        layoutWidth: Int,
+        layoutHeight: Int
+    ) {
+        val x1 = 0f
+        val y1 = 0f
+        val x2 = 1f
+        val y2 = 1f
+        val minx = 0f
+        val maxy = 0f
+        // Horizontal line
+        var text = "" + (0.5 + 100 * (x - viewWidth / 2) /
+            (layoutWidth - viewWidth)).toInt() / 100.0f
+        getTextBounds(text, mTextPaint)
+        var off = x / 2 - mBounds.width() / 2
+        canvas.drawText(text, off + minx, y - 20, mTextPaint)
+        canvas.drawLine(
+            x, y,
+            Math.min(x1, x2), y, mPaintGraph
+        )
+
+        // Vertical line
+        text = "" + (0.5 + 100 * (y - viewHeight / 2) /
+            (layoutHeight - viewHeight)).toInt() / 100.0f
+        getTextBounds(text, mTextPaint)
+        off = y / 2 - mBounds.height() / 2
+        canvas.drawText(text, x + 5, maxy - off, mTextPaint)
+        canvas.drawLine(
+            x, y,
+            x, Math.max(y1, y2), mPaintGraph
+        )
+    }
+
+    private fun drawRectangle(canvas: Canvas, motionController: Motion) {
+        mPath!!.reset()
+        val rectFrames = 50
+        for (i in 0..rectFrames) {
+            val p = i / rectFrames.toFloat()
+            motionController.buildRect(p, mRectangle, 0)
+            mPath!!.moveTo(mRectangle[0], mRectangle[1])
+            mPath!!.lineTo(mRectangle[2], mRectangle[3])
+            mPath!!.lineTo(mRectangle[4], mRectangle[5])
+            mPath!!.lineTo(mRectangle[6], mRectangle[7])
+            mPath!!.close()
+        }
+        mPaint.color = 0x44000000
+        canvas.translate(2f, 2f)
+        canvas.drawPath(mPath!!, mPaint)
+        canvas.translate(-2f, -2f)
+        mPaint.color = -0x10000
+        canvas.drawPath(mPath!!, mPaint)
+    }
+
+    companion object {
+        const val DEBUG_SHOW_NONE = 0
+        const val DEBUG_SHOW_PROGRESS = 1
+        const val DEBUG_SHOW_PATH = 2
+        const val MAX_KEY_FRAMES = 50
+        private const val DEBUG_PATH_TICKS_PER_MS = 16
+    }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt
index 3c7ee4d..a0bb06f 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt
@@ -189,7 +189,7 @@
 
     internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f })
 
-    internal var velocityThreshold by mutableStateOf(0f)
+    internal var velocityThreshold by mutableFloatStateOf(0f)
 
     internal var resistance: ResistanceConfig? by mutableStateOf(null)
 
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/KeyFrames.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/KeyFrames.java
index f7c980f..517236c 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/KeyFrames.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/KeyFrames.java
@@ -91,19 +91,30 @@
                         tagName = parser.getName();
 
                         if (sKeyMakers.containsKey(tagName)) {
-                            try {
-                                Constructor<? extends Key> keyMaker = sKeyMakers.get(tagName);
-                                if (keyMaker != null) {
-                                    key = keyMaker.newInstance();
-                                    key.load(context, Xml.asAttributeSet(parser));
-                                    addKey(key);
-                                } else {
+
+                            switch (tagName) {
+                                case KeyAttributes.NAME:
+                                    key = new KeyAttributes();
+                                    break;
+                                case KeyPosition.NAME:
+                                    key = new KeyPosition();
+                                    break;
+                                case KeyCycle.NAME:
+                                    key = new KeyCycle();
+                                    break;
+                                case KeyTimeCycle.NAME:
+                                    key = new KeyTimeCycle();
+                                    break;
+                                case KeyTrigger.NAME:
+                                    key = new KeyTrigger();
+                                    break;
+                                default:
                                     throw new NullPointerException(
-                                            "Keymaker for " + tagName + " not found");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "unable to create ", e);
+                                            "Key " + tagName + " not found");
                             }
+                            key.load(context, Xml.asAttributeSet(parser));
+                            addKey(key);
+
                         } else if (tagName.equalsIgnoreCase(CUSTOM_ATTRIBUTE)) {
                             if (key != null && key.mCustomConstraints != null) {
                                 ConstraintAttribute.parse(context, parser, key.mCustomConstraints);
diff --git a/contentpager/contentpager/src/main/AndroidManifest.xml b/contentpager/contentpager/src/main/AndroidManifest.xml
deleted file mode 100644
index 95c4426..0000000
--- a/contentpager/contentpager/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/coordinatorlayout/coordinatorlayout/src/main/AndroidManifest.xml b/coordinatorlayout/coordinatorlayout/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/coordinatorlayout/coordinatorlayout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/core/core-animation-integration-tests/testapp/src/main/AndroidManifest.xml b/core/core-animation-integration-tests/testapp/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/core/core-animation-integration-tests/testapp/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/core/core-animation-testing/src/main/AndroidManifest.xml b/core/core-animation-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/core/core-animation-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/core/core-animation/src/main/AndroidManifest.xml b/core/core-animation/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/core/core-animation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/core/core-appdigest/src/main/AndroidManifest.xml b/core/core-appdigest/src/main/AndroidManifest.xml
deleted file mode 100644
index 3031a00..0000000
--- a/core/core-appdigest/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/core/core-ktx/src/main/AndroidManifest.xml b/core/core-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index cc947c5..0000000
--- a/core/core-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-<manifest />
diff --git a/core/core-role/src/main/AndroidManifest.xml b/core/core-role/src/main/AndroidManifest.xml
deleted file mode 100644
index 5e016ed..0000000
--- a/core/core-role/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 7ce3b2f..c5f7d8c 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1078,6 +1078,7 @@
     method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
     method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
     method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.view.Display getDisplay(@DisplayContext android.content.Context);
     method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
     method public static java.io.File![] getExternalCacheDirs(android.content.Context);
     method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 77a0578..a9d425f 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1078,6 +1078,7 @@
     method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
     method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
     method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.view.Display getDisplay(@DisplayContext android.content.Context);
     method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
     method public static java.io.File![] getExternalCacheDirs(android.content.Context);
     method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 9d411f1..6a02dd0 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1195,6 +1195,7 @@
     method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
     method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
     method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.view.Display getDisplay(@DisplayContext android.content.Context);
     method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
     method public static java.io.File![] getExternalCacheDirs(android.content.Context);
     method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
diff --git a/core/core/src/androidTest/AndroidManifest.xml b/core/core/src/androidTest/AndroidManifest.xml
index 8e9f2ea..2688f36 100644
--- a/core/core/src/androidTest/AndroidManifest.xml
+++ b/core/core/src/androidTest/AndroidManifest.xml
@@ -137,6 +137,10 @@
             android:exported="true" />
 
         <activity
+            android:name="androidx.core.view.inputmethod.ImeSecondarySplitViewCompatTestActivity"
+            android:exported="true" />
+
+        <activity
             android:name="androidx.core.view.inputmethod.ImeSecondarySplitTestActivity"
             android:exported="true" />
 
diff --git a/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
index 85db291..42fce77 100644
--- a/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
@@ -66,6 +66,7 @@
 import static android.content.Context.WIFI_P2P_SERVICE;
 import static android.content.Context.WIFI_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -137,6 +138,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -146,8 +148,10 @@
 
 import androidx.annotation.OptIn;
 import androidx.core.app.NotificationManagerCompat;
+import androidx.core.hardware.display.DisplayManagerCompat;
 import androidx.core.os.BuildCompat;
 import androidx.core.test.R;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -601,4 +605,58 @@
                             Manifest.permission.POST_NOTIFICATIONS));
         }
     }
+
+    @Test
+    public void testGetDisplayFromActivity() {
+        final Display actualDisplay = ContextCompat.getDisplay(mContext);
+        if (Build.VERSION.SDK_INT >= 30) {
+            assertEquals(mContext.getDisplay(), actualDisplay);
+        } else {
+            final WindowManager windowManager =
+                    (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
+            assertEquals(actualDisplay, windowManager.getDefaultDisplay());
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 17)
+    public void testGetDisplayFromDisplayContext() {
+        final DisplayManagerCompat displayManagerCompat = DisplayManagerCompat
+                .getInstance(mContext);
+        final Display defaultDisplay =  displayManagerCompat.getDisplay(Display.DEFAULT_DISPLAY);
+        final Context displayContext = mContext.createDisplayContext(defaultDisplay);
+
+        assertEquals(ContextCompat.getDisplay(displayContext), defaultDisplay);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 30)
+    public void testGetDisplayFromWindowContext() {
+        final Context windowContext = mContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+
+        assertEquals(ContextCompat.getDisplay(windowContext), windowContext.getDisplay());
+    }
+
+    @Test
+    public void testGetDisplayFromApplication() {
+        final Context applicationContext = ApplicationProvider.getApplicationContext();
+        final Context spyContext = spy(applicationContext);
+        final Display actualDisplay = ContextCompat.getDisplay(spyContext);
+
+        if (Build.VERSION.SDK_INT >= 30) {
+            verify(spyContext).getSystemService(eq(DisplayManager.class));
+
+            final Display defaultDisplay = DisplayManagerCompat.getInstance(spyContext)
+                    .getDisplay(Display.DEFAULT_DISPLAY);
+            assertEquals(defaultDisplay, actualDisplay);
+        } else {
+            final WindowManager windowManager =
+                    (WindowManager) spyContext.getSystemService(WINDOW_SERVICE);
+            // Don't verify if the returned display is the same instance because Application is
+            // not a DisplayContext and the framework always create a fallback Display for
+            // the Context that not associated with a Display.
+            assertEquals(windowManager.getDefaultDisplay().getDisplayId(),
+                    actualDisplay.getDisplayId());
+        }
+    }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeSecondarySplitViewCompatTestActivity.java b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeSecondarySplitViewCompatTestActivity.java
new file mode 100644
index 0000000..5bcd1e28
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeSecondarySplitViewCompatTestActivity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view.inputmethod;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.R;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+@RequiresApi(30)
+public class ImeSecondarySplitViewCompatTestActivity extends Activity {
+
+    EditText mEditText;
+
+    Button mHideImeButton;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ime_secondary_split_test_activity);
+        mEditText = findViewById(R.id.edit_text_id);
+        mHideImeButton = findViewById(R.id.hide_ime_id);
+        mHideImeButton.setOnClickListener(view -> hideIme());
+    }
+
+    private void hideIme() {
+        // Use deprecated WindowInsetsControllerCompat method to attempt to hide ime.
+        WindowInsetsControllerCompat insetsController =
+                ViewCompat.getWindowInsetsController(mEditText);
+        if (insetsController != null) {
+            insetsController.hide(WindowInsetsCompat.Type.ime());
+        }
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeViewCompatMultiWindowTest.java b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeViewCompatMultiWindowTest.java
new file mode 100644
index 0000000..c5fa2ef
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeViewCompatMultiWindowTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view.inputmethod;
+
+import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.RemoteException;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import androidx.core.view.WindowInsetsCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+import androidx.testutils.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+@SdkSuppress(minSdkVersion = 30)
+public class ImeViewCompatMultiWindowTest extends BaseInstrumentationTestCase<ImeBaseSplitTestActivity> {
+
+    private static final long ACTIVITY_LAUNCH_TIMEOUT_MS = 10000;
+    private static final long VISIBILITY_TIMEOUT_MS = 2000;
+    private static final long FIND_OBJECT_TIMEOUT_MS = 5000;
+    private static final long CLICK_DURATION_MS = 200;
+
+    private static final String TEST_APP = "androidx.core.test";
+
+    private Activity mActivity;
+
+    private UiDevice mDevice;
+
+    public ImeViewCompatMultiWindowTest() {
+        super(ImeBaseSplitTestActivity.class);
+    }
+
+    @Before
+    public void setup() throws RemoteException {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.wakeUp();
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    /**
+     * This test is using a deprecated codepath that doesn't support the workaround, so it is
+     * expected to fail hiding the IME.
+     * If this test begins failing on a new API version (that is, an assertion error is no longer
+     * being thrown), it is likely that the workaround is no longer needed on that API version:
+     * b/280532442
+     */
+    @Test(expected = AssertionError.class)
+    @SdkSuppress(minSdkVersion = 30)
+    public void testImeShowAndHide_splitScreen() {
+        if (Build.VERSION.SDK_INT < 32) {
+            // FLAG_ACTIVITY_LAUNCH_ADJACENT is not support before Sdk 32, using the
+            // GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN instead.
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
+        }
+
+        // Launch ime test activity in secondary split.
+        Intent intent = new Intent(mActivity, ImeSecondarySplitViewCompatTestActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        mActivity.startActivity(intent);
+
+        assertTrue("Test app is not visible after launching activity",
+                mDevice.wait(Until.hasObject(By.pkg(TEST_APP)), ACTIVITY_LAUNCH_TIMEOUT_MS));
+
+        UiObject2 editText = waitForFindObject("edit_text_id");
+        editText.click(CLICK_DURATION_MS);
+
+        WindowManager wm = mActivity.getSystemService(WindowManager.class);
+        PollingCheck.waitFor(VISIBILITY_TIMEOUT_MS, () -> {
+            WindowInsets insets = wm.getCurrentWindowMetrics().getWindowInsets();
+            return insets.isVisible(WindowInsetsCompat.Type.ime());
+        });
+
+        UiObject2 hideImeButton = waitForFindObject("hide_ime_id");
+        hideImeButton.click();
+
+        PollingCheck.waitFor(VISIBILITY_TIMEOUT_MS, () -> {
+            WindowInsets insets = wm.getCurrentWindowMetrics().getWindowInsets();
+            return !insets.isVisible(WindowInsetsCompat.Type.ime());
+        });
+    }
+
+    private UiObject2 waitForFindObject(String resId) {
+        final UiObject2 object =
+                mDevice.wait(Until.findObject(By.res(TEST_APP, resId)), FIND_OBJECT_TIMEOUT_MS);
+        assertNotNull("Find object fail", object);
+        return object;
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/content/ContextCompat.java b/core/core/src/main/java/androidx/core/content/ContextCompat.java
index cd4e613..172b452 100644
--- a/core/core/src/main/java/androidx/core/content/ContextCompat.java
+++ b/core/core/src/main/java/androidx/core/content/ContextCompat.java
@@ -70,6 +70,7 @@
 
 import android.accounts.AccountManager;
 import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -131,6 +132,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.TypedValue;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -140,6 +142,7 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorRes;
+import androidx.annotation.DisplayContext;
 import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
@@ -753,6 +756,28 @@
     }
 
     /**
+     * Get the display this context is associated with.
+     * <p>
+     * Applications must use this method with {@link Activity} or a context associated with a
+     * {@link Display} via {@link Context#createDisplayContext(Display)} or
+     * {@link Context#createWindowContext(Display, int, Bundle)}, or the reported {@link Display}
+     * instance is not reliable. </p>
+     *
+     * @param context Context to obtain the associated display
+     * @return The display associated with the Context.
+     */
+    @NonNull
+    public static Display getDisplay(@NonNull @DisplayContext Context context) {
+        if (Build.VERSION.SDK_INT >= 30) {
+            return Api30Impl.getDisplayNoCrash(context);
+        } else {
+            final WindowManager windowManager =
+                    (WindowManager) context.getSystemService(WINDOW_SERVICE);
+            return windowManager.getDefaultDisplay();
+        }
+    }
+
+    /**
      * Return the handle to a system-level service by class.
      *
      * @param context      Context to retrieve service from.
@@ -1113,6 +1138,19 @@
         static String getAttributionTag(Context obj) {
             return obj.getAttributionTag();
         }
+
+        @DoNotInline
+        static Display getDisplayNoCrash(Context obj) {
+            try {
+                return obj.getDisplay();
+            } catch (UnsupportedOperationException e) {
+                // Provide a fallback display if the context is not associated with any display.
+                Log.w(TAG, "The context:" + obj + " is not associated with any display. Return a "
+                        + "fallback display instead.");
+                return obj.getSystemService(DisplayManager.class)
+                        .getDisplay(Display.DEFAULT_DISPLAY);
+            }
+        }
     }
 
     @RequiresApi(33)
diff --git a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
index 5ccc392..dee46e1 100644
--- a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
@@ -215,7 +215,7 @@
                 // callback current controllable insets. Adding the listener here to check if
                 // ime inset is controllable.
                 insetsController.addOnControllableInsetsChangedListener(listener);
-                if (!isImeInsetsControllable.get()) {
+                if (!isImeInsetsControllable.get() && mView != null) {
                     final InputMethodManager imm = (InputMethodManager) mView.getContext()
                             .getSystemService(Context.INPUT_METHOD_SERVICE);
                     // This is a backport when the app is in multi-windowing mode, it cannot
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 768e166..3837165 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -814,13 +814,55 @@
     /**
      * Class with information if a node is a collection.
      * <p>
-     * A collection of items has rows and columns and may be hierarchical.
-     * For example, a horizontal list is a collection with one column, as
-     * many rows as the list items, and is not hierarchical; A table is a
-     * collection with several rows, several columns, and is not hierarchical;
-     * A vertical tree is a hierarchical collection with one column and
-     * as many rows as the first level children.
-     * </p>
+     * A collection of items has rows and columns and may be marked as hierarchical.
+     *
+     * <p>
+     * For example, a list where the items are placed in a vertical layout is a collection with one
+     * column and as many rows as the list items. This collection has 3 rows and 1 column and should
+     * not be marked as hierarchical since items do not exist at different levels/ranks and there
+     * are no nested collections.
+     * <ul>
+     *     <li>Item 1</li>
+     *     <li>Item 2</li>
+     *     <li>Item 3</li>
+     * </ul>
+     *
+     * <p>
+     * A table is a collection with several rows and several columns. This collection has 2 rows and
+     * 3 columns and is not marked as hierarchical:
+     *<table>
+     *   <tr>
+     *     <td>Item 1</td>
+     *     <td>Item 2</td>
+     *     <td>Item 3</td>
+     *   </tr>
+     *   <tr>
+     *     <td>Item 4</td>
+     *     <td>Item 5</td>
+     *     <td>Item 6</td>
+     *   </tr>
+     * </table>
+     *
+     * <p>
+     * Nested collections could be marked as hierarchical. To add outer and inner collections to the
+     * same hierarchy, mark them both as hierarchical.
+     *
+     * <p> For example, if you have a collection with two lists - this collection has an outer
+     * list with 3 rows and 1 column and an inner list within "Item 2" with 2 rows and 1 -
+     * you can mark both the outer list and the inner list as hierarchical to make them part of
+     * the same hierarchy. If a collection does not have any ancestor or descendant hierarchical
+     * collections, it does not need to be marked as hierarchical.
+     *  <ul>
+     *      <li>Item 1</li>
+     *      <li> Item 2
+     *          <ul>
+     *              <li>Item 2A</li>
+     *              <li>Item 2B</li>
+     *          </ul>
+     *      </li>
+     *      <li>Item 3</li>
+     *  </ul>
+     *
      * <p>
      * To be a valid list, a collection has 1 row and any number of columns or 1 column and any
      * number of rows.
diff --git a/cursoradapter/cursoradapter/src/main/AndroidManifest.xml b/cursoradapter/cursoradapter/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/cursoradapter/cursoradapter/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/customview/customview/src/main/AndroidManifest.xml b/customview/customview/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/customview/customview/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index ce7f919..10836a5 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -122,6 +122,14 @@
 WARN: Missing @param tag for parameter `autoMirror` of function androidx\.compose\.ui\.graphics\.vector//rememberVectorPainter/\#androidx\.compose\.ui\.unit\.Dp\#androidx\.compose\.ui\.unit\.Dp\#kotlin\.Float\#kotlin\.Float\#kotlin\.String\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.BlendMode\#kotlin\.Boolean\#kotlin\.Function[0-9]+\[kotlin\.Float,kotlin\.Float,kotlin\.Unit\]/PointingToDeclaration/
 WARN: Missing @param tag for parameter `trimPathEnd` of function androidx\.compose\.ui\.graphics\.vector/ImageVector\.Builder/addPath/\#kotlin\.collections\.List\[androidx\.compose\.ui\.graphics\.vector\.PathNode\]\#androidx\.compose\.ui\.graphics\.PathFillType\#kotlin\.String\#androidx\.compose\.ui\.graphics\.Brush\?\#kotlin\.Float\#androidx\.compose\.ui\.graphics\.Brush\?\#kotlin\.Float\#kotlin\.Float\#androidx\.compose\.ui\.graphics\.StrokeCap\#androidx\.compose\.ui\.graphics\.StrokeJoin\#kotlin\.Float\#kotlin\.Float\#kotlin\.Float\#kotlin\.Float/PointingToDeclaration/
 WARN: Missing @param tag for parameter `pathFillType` of function androidx\.compose\.ui\.graphics\.vector/ImageVector\.Builder/path/androidx\.compose\.ui\.graphics\.vector\.ImageVector\.Builder\#kotlin\.String\#androidx\.compose\.ui\.graphics\.Brush\?\#kotlin\.Float\#androidx\.compose\.ui\.graphics\.Brush\?\#kotlin\.Float\#kotlin\.Float\#androidx\.compose\.ui\.graphics\.StrokeCap\#androidx\.compose\.ui\.graphics\.StrokeJoin\#kotlin\.Float\#androidx\.compose\.ui\.graphics\.PathFillType\#kotlin\.Function[0-9]+\[androidx\.compose\.ui\.graphics\.vector\.PathBuilder,kotlin\.Unit\]/PointingToDeclaration/
+WARN: Failed to resolve `@see SdkSandboxManager\.startSdkSandboxActivity`!
+Did you mean SdkSandboxManager\#startSdkSandboxActivity\?
+WARN: Failed to resolve `@see SdkSandboxActivityHandler\.onActivityCreated`!
+Did you mean SdkSandboxActivityHandler\#onActivityCreated\?
+WARN: Failed to resolve `@see SdkSandboxController\.registerSdkSandboxActivityHandler`!
+Did you mean SdkSandboxController\#registerSdkSandboxActivityHandler\?
+WARN: Failed to resolve `@see SdkSandboxController\.unregisterSdkSandboxActivityHandler`!
+Did you mean SdkSandboxController\#unregisterSdkSandboxActivityHandler\?
 WARN: Missing @param tag for parameter `position` of function androidx\.compose\.ui\.layout/Placeable/placeAt/\#androidx\.compose\.ui\.unit\.IntOffset\#kotlin\.Float\#kotlin\.Function[0-9]+\[androidx\.compose\.ui\.graphics\.GraphicsLayerScope,kotlin\.Unit\]\?/PointingToDeclaration/
 WARN: Missing @param tag for parameter `x` of function androidx\.compose\.ui\.layout/Placeable\.PlacementScope/place/androidx\.compose\.ui\.layout\.Placeable\#kotlin\.Int\#kotlin\.Int\#kotlin\.Float/PointingToDeclaration/
 WARN: Missing @param tag for parameter `y` of function androidx\.compose\.ui\.layout/Placeable\.PlacementScope/place/androidx\.compose\.ui\.layout\.Placeable\#kotlin\.Int\#kotlin\.Int\#kotlin\.Float/PointingToDeclaration/
@@ -189,6 +197,7 @@
 WARN: Missing @param tag for parameter `content` of function androidx\.wear\.compose\.material[0-9]+//MaterialTheme/\#androidx\.wear\.compose\.material[0-9]+\.ColorScheme\#androidx\.wear\.compose\.material[0-9]+\.Typography\#androidx\.wear\.compose\.material[0-9]+\.Shapes\#kotlin\.Function[0-9]+\[kotlin\.Unit\]/PointingToDeclaration/
 WARN: Missing @param tag for parameter `content` of function androidx\.wear\.compose\.material[0-9]+//OutlinedButton/\#kotlin\.Function[0-9]+\[kotlin\.Unit\]\#androidx\.compose\.ui\.Modifier\#kotlin\.Boolean\#androidx\.compose\.ui\.graphics\.Shape\#androidx\.wear\.compose\.material[0-9]+\.ButtonColors\#androidx\.compose\.foundation\.BorderStroke\?\#androidx\.compose\.foundation\.layout\.PaddingValues\#androidx\.compose\.foundation\.interaction\.MutableInteractionSource\#androidx\.compose\.ui\.semantics\.Role\?\#kotlin\.Function[0-9]+\[androidx\.compose\.foundation\.layout\.RowScope,kotlin\.Unit\]/PointingToDeclaration/
 WARN: Missing @param tag for parameter `enabled` of function androidx\.wear\.compose\.material[0-9]+/ButtonDefaults/outlinedButtonBorder/\#kotlin\.Boolean\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.unit\.Dp/PointingToDeclaration/
+WARN: Missing @param tag for parameter `enabled` of function androidx\.wear\.compose\.material[0-9]+/IconButtonDefaults/outlinedIconButtonBorder/\#kotlin\.Boolean\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.unit\.Dp/PointingToDeclaration/
 WARN: Unable to find what is referred to by
 # > Task :docs-tip-of-tree:dackkaDocs
 WARN\: Failed to resolve \`\@see SplitAttributes\.shouldExpandSecondaryContainer\`\!
diff --git a/development/project-creator/compose-template/groupId/artifactId/build.gradle b/development/project-creator/compose-template/groupId/artifactId/build.gradle
index a47774d..e09ec31 100644
--- a/development/project-creator/compose-template/groupId/artifactId/build.gradle
+++ b/development/project-creator/compose-template/groupId/artifactId/build.gradle
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXComposePlugin
 import androidx.build.LibraryType
+import androidx.build.KmpPlatformsKt
 
 plugins {
     id("AndroidXPlugin")
@@ -24,68 +24,75 @@
     id("org.jetbrains.kotlin.android")
 }
 
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
+androidXMultiplatform {
+    android()
+    if (desktopEnabled) desktop()
 
-
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block below
-         */
-        implementation(libs.kotlinStdlibCommon)
-
-        api("androidx.annotation:annotation:1.1.0")
-
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.truth)
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
             }
+        }
+        androidMain.dependencies {
+        }
 
-            androidMain.dependencies {
+        commonTest {
+            dependencies {
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+            dependencies {
+                implementation(libs.testRules)
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+                implementation(libs.truth)
+            }
+        }
+
+
+        androidMain {
+            dependsOn(jvmMain)
+            dependencies {
                 api("androidx.annotation:annotation:1.1.0")
             }
+        }
 
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
+        if (desktopEnabled) {
+            desktopMain {
+                dependsOn(jvmMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+                }
             }
+        }
 
-            test.dependencies {
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.junit)
                 implementation(libs.truth)
             }
+        }
 
-            androidAndroidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
+        if (desktopEnabled) {
+            desktopTest {
+                dependsOn(jvmTest)
+                dependsOn(desktopMain)
+                dependencies {
+                }
             }
         }
     }
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 569d1ac..d082aaf 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -8,10 +8,10 @@
 }
 
 dependencies {
-    docs("androidx.activity:activity:1.8.0-alpha03")
-    docs("androidx.activity:activity-compose:1.8.0-alpha03")
-    samples("androidx.activity:activity-compose-samples:1.8.0-alpha03")
-    docs("androidx.activity:activity-ktx:1.8.0-alpha03")
+    docs("androidx.activity:activity:1.8.0-alpha04")
+    docs("androidx.activity:activity-compose:1.8.0-alpha04")
+    samples("androidx.activity:activity-compose-samples:1.8.0-alpha04")
+    docs("androidx.activity:activity-ktx:1.8.0-alpha04")
     docs("androidx.ads:ads-identifier:1.0.0-alpha05")
     docs("androidx.ads:ads-identifier-common:1.0.0-alpha05")
     docs("androidx.ads:ads-identifier-provider:1.0.0-alpha05")
@@ -55,55 +55,55 @@
     docs("androidx.cardview:cardview:1.0.0")
     kmpDocs("androidx.collection:collection:1.3.0-alpha03")
     docs("androidx.collection:collection-ktx:1.3.0-alpha03")
-    docs("androidx.compose.animation:animation:1.5.0-alpha03")
-    docs("androidx.compose.animation:animation-core:1.5.0-alpha03")
-    docs("androidx.compose.animation:animation-graphics:1.5.0-alpha03")
-    samples("androidx.compose.animation:animation-samples:1.5.0-alpha03")
-    samples("androidx.compose.animation:animation-core-samples:1.5.0-alpha03")
-    samples("androidx.compose.animation:animation-graphics-samples:1.5.0-alpha03")
-    docs("androidx.compose.foundation:foundation:1.5.0-alpha03")
-    docs("androidx.compose.foundation:foundation-layout:1.5.0-alpha03")
-    samples("androidx.compose.foundation:foundation-layout-samples:1.5.0-alpha03")
-    samples("androidx.compose.foundation:foundation-samples:1.5.0-alpha03")
-    docs("androidx.compose.material3:material3:1.1.0-rc01")
-    samples("androidx.compose.material3:material3-samples:1.1.0-rc01")
-    docs("androidx.compose.material3:material3-window-size-class:1.1.0-rc01")
-    samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-rc01")
-    docs("androidx.compose.material:material:1.5.0-alpha03")
-    docs("androidx.compose.material:material-icons-core:1.5.0-alpha03")
-    samples("androidx.compose.material:material-icons-core-samples:1.5.0-alpha03")
-    docs("androidx.compose.material:material-ripple:1.5.0-alpha03")
-    samples("androidx.compose.material:material-samples:1.5.0-alpha03")
-    docs("androidx.compose.runtime:runtime:1.5.0-alpha03")
-    docs("androidx.compose.runtime:runtime-livedata:1.5.0-alpha03")
-    samples("androidx.compose.runtime:runtime-livedata-samples:1.5.0-alpha03")
-    docs("androidx.compose.runtime:runtime-rxjava2:1.5.0-alpha03")
-    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.5.0-alpha03")
-    docs("androidx.compose.runtime:runtime-rxjava3:1.5.0-alpha03")
-    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.5.0-alpha03")
-    docs("androidx.compose.runtime:runtime-saveable:1.5.0-alpha03")
-    samples("androidx.compose.runtime:runtime-saveable-samples:1.5.0-alpha03")
-    samples("androidx.compose.runtime:runtime-samples:1.5.0-alpha03")
+    docs("androidx.compose.animation:animation:1.5.0-alpha04")
+    docs("androidx.compose.animation:animation-core:1.5.0-alpha04")
+    docs("androidx.compose.animation:animation-graphics:1.5.0-alpha04")
+    samples("androidx.compose.animation:animation-samples:1.5.0-alpha04")
+    samples("androidx.compose.animation:animation-core-samples:1.5.0-alpha04")
+    samples("androidx.compose.animation:animation-graphics-samples:1.5.0-alpha04")
+    docs("androidx.compose.foundation:foundation:1.5.0-alpha04")
+    docs("androidx.compose.foundation:foundation-layout:1.5.0-alpha04")
+    samples("androidx.compose.foundation:foundation-layout-samples:1.5.0-alpha04")
+    samples("androidx.compose.foundation:foundation-samples:1.5.0-alpha04")
+    docs("androidx.compose.material3:material3:1.2.0-alpha01")
+    samples("androidx.compose.material3:material3-samples:1.2.0-alpha01")
+    docs("androidx.compose.material3:material3-window-size-class:1.2.0-alpha01")
+    samples("androidx.compose.material3:material3-window-size-class-samples:1.2.0-alpha01")
+    docs("androidx.compose.material:material:1.5.0-alpha04")
+    docs("androidx.compose.material:material-icons-core:1.5.0-alpha04")
+    samples("androidx.compose.material:material-icons-core-samples:1.5.0-alpha04")
+    docs("androidx.compose.material:material-ripple:1.5.0-alpha04")
+    samples("androidx.compose.material:material-samples:1.5.0-alpha04")
+    docs("androidx.compose.runtime:runtime:1.5.0-alpha04")
+    docs("androidx.compose.runtime:runtime-livedata:1.5.0-alpha04")
+    samples("androidx.compose.runtime:runtime-livedata-samples:1.5.0-alpha04")
+    docs("androidx.compose.runtime:runtime-rxjava2:1.5.0-alpha04")
+    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.5.0-alpha04")
+    docs("androidx.compose.runtime:runtime-rxjava3:1.5.0-alpha04")
+    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.5.0-alpha04")
+    docs("androidx.compose.runtime:runtime-saveable:1.5.0-alpha04")
+    samples("androidx.compose.runtime:runtime-saveable-samples:1.5.0-alpha04")
+    samples("androidx.compose.runtime:runtime-samples:1.5.0-alpha04")
     docs("androidx.compose.runtime:runtime-tracing:1.0.0-alpha03")
-    docs("androidx.compose.ui:ui:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-geometry:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-graphics:1.5.0-alpha03")
-    samples("androidx.compose.ui:ui-graphics-samples:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-test:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-test-junit4:1.5.0-alpha03")
-    samples("androidx.compose.ui:ui-test-samples:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-text:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-text-google-fonts:1.5.0-alpha03")
-    samples("androidx.compose.ui:ui-text-samples:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-tooling:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-tooling-data:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-tooling-preview:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-unit:1.5.0-alpha03")
-    samples("androidx.compose.ui:ui-unit-samples:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-util:1.5.0-alpha03")
-    docs("androidx.compose.ui:ui-viewbinding:1.5.0-alpha03")
-    samples("androidx.compose.ui:ui-viewbinding-samples:1.5.0-alpha03")
-    samples("androidx.compose.ui:ui-samples:1.5.0-alpha03")
+    docs("androidx.compose.ui:ui:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-geometry:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-graphics:1.5.0-alpha04")
+    samples("androidx.compose.ui:ui-graphics-samples:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-test:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-test-junit4:1.5.0-alpha04")
+    samples("androidx.compose.ui:ui-test-samples:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-text:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-text-google-fonts:1.5.0-alpha04")
+    samples("androidx.compose.ui:ui-text-samples:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-tooling:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-tooling-data:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-tooling-preview:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-unit:1.5.0-alpha04")
+    samples("androidx.compose.ui:ui-unit-samples:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-util:1.5.0-alpha04")
+    docs("androidx.compose.ui:ui-viewbinding:1.5.0-alpha04")
+    samples("androidx.compose.ui:ui-viewbinding-samples:1.5.0-alpha04")
+    samples("androidx.compose.ui:ui-samples:1.5.0-alpha04")
     docs("androidx.concurrent:concurrent-futures:1.2.0-alpha01")
     docs("androidx.concurrent:concurrent-futures-ktx:1.2.0-alpha01")
     docs("androidx.constraintlayout:constraintlayout:2.2.0-alpha09")
@@ -116,11 +116,11 @@
     docs("androidx.core:core-google-shortcuts:1.1.0")
     docs("androidx.core:core-performance:1.0.0-alpha03")
     samples("androidx.core:core-performance-samples:1.0.0-alpha03")
-    docs("androidx.core:core-remoteviews:1.0.0-beta03")
+    docs("androidx.core:core-remoteviews:1.0.0-beta04")
     docs("androidx.core:core-role:1.2.0-alpha01")
     docs("androidx.core:core-animation:1.0.0-beta02")
     docs("androidx.core:core-animation-testing:1.0.0-beta01")
-    docs("androidx.core:core:1.12.0-alpha03")
+    docs("androidx.core:core:1.12.0-alpha04")
     docs("androidx.core:core-ktx:1.12.0-alpha03")
     docs("androidx.core:core-splashscreen:1.1.0-alpha01")
     docs("androidx.core:core-testing:1.12.0-alpha03")
@@ -144,27 +144,28 @@
     docs("androidx.drawerlayout:drawerlayout:1.2.0")
     docs("androidx.dynamicanimation:dynamicanimation:1.1.0-alpha02")
     docs("androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03")
-    docs("androidx.emoji2:emoji2:1.4.0-beta02")
-    docs("androidx.emoji2:emoji2-bundled:1.4.0-beta02")
-    docs("androidx.emoji2:emoji2-emojipicker:1.4.0-beta02")
-    docs("androidx.emoji2:emoji2-views:1.4.0-beta02")
-    docs("androidx.emoji2:emoji2-views-helper:1.4.0-beta02")
+    docs("androidx.emoji2:emoji2:1.4.0-beta03")
+    docs("androidx.emoji2:emoji2-bundled:1.4.0-beta03")
+    docs("androidx.emoji2:emoji2-emojipicker:1.4.0-beta03")
+    docs("androidx.emoji2:emoji2-views:1.4.0-beta03")
+    docs("androidx.emoji2:emoji2-views-helper:1.4.0-beta03")
     docs("androidx.emoji:emoji:1.2.0-alpha03")
     docs("androidx.emoji:emoji-appcompat:1.2.0-alpha03")
     docs("androidx.emoji:emoji-bundled:1.2.0-alpha03")
     docs("androidx.enterprise:enterprise-feedback:1.1.0")
     docs("androidx.enterprise:enterprise-feedback-testing:1.1.0")
     docs("androidx.exifinterface:exifinterface:1.3.6")
-    docs("androidx.fragment:fragment:1.6.0-beta01")
-    docs("androidx.fragment:fragment-ktx:1.6.0-beta01")
-    docs("androidx.fragment:fragment-testing:1.6.0-beta01")
-    docs("androidx.glance:glance:1.0.0-alpha05")
-    docs("androidx.glance:glance-appwidget:1.0.0-alpha05")
-    docs("androidx.glance:glance-appwidget-preview:1.0.0-alpha05")
+    docs("androidx.fragment:fragment:1.6.0-rc01")
+    docs("androidx.fragment:fragment-ktx:1.6.0-rc01")
+    docs("androidx.fragment:fragment-testing:1.6.0-rc01")
+    docs("androidx.glance:glance:1.0.0-beta01")
+    samples("androidx.glance:glance-appwidget-samples:1.0.0-beta01")
+    docs("androidx.glance:glance-appwidget:1.0.0-beta01")
+    docs("androidx.glance:glance-appwidget-preview:1.0.0-alpha06")
     docs("androidx.glance:glance-appwidget-proto:1.0.0-alpha03")
-    docs("androidx.glance:glance-preview:1.0.0-alpha05")
-    docs("androidx.glance:glance-wear-tiles:1.0.0-alpha05")
-    docs("androidx.glance:glance-wear-tiles-preview:1.0.0-alpha05")
+    docs("androidx.glance:glance-preview:1.0.0-alpha06")
+    docs("androidx.glance:glance-wear-tiles:1.0.0-alpha06")
+    docs("androidx.glance:glance-wear-tiles-preview:1.0.0-alpha06")
     docs("androidx.graphics:graphics-core:1.0.0-alpha03")
     docs("androidx.gridlayout:gridlayout:1.1.0-alpha01")
     docs("androidx.health.connect:connect-client:1.0.0-alpha11")
@@ -239,19 +240,19 @@
     docs("androidx.mediarouter:mediarouter:1.6.0-alpha03")
     docs("androidx.mediarouter:mediarouter-testing:1.6.0-alpha03")
     docs("androidx.metrics:metrics-performance:1.0.0-alpha04")
-    docs("androidx.navigation:navigation-common:2.6.0-beta01")
-    docs("androidx.navigation:navigation-common-ktx:2.6.0-beta01")
-    docs("androidx.navigation:navigation-compose:2.6.0-beta01")
-    samples("androidx.navigation:navigation-compose-samples:2.6.0-beta01")
-    docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-beta01")
-    docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-beta01")
-    docs("androidx.navigation:navigation-fragment:2.6.0-beta01")
-    docs("androidx.navigation:navigation-fragment-ktx:2.6.0-beta01")
-    docs("androidx.navigation:navigation-runtime:2.6.0-beta01")
-    docs("androidx.navigation:navigation-runtime-ktx:2.6.0-beta01")
-    docs("androidx.navigation:navigation-testing:2.6.0-beta01")
-    docs("androidx.navigation:navigation-ui:2.6.0-beta01")
-    docs("androidx.navigation:navigation-ui-ktx:2.6.0-beta01")
+    docs("androidx.navigation:navigation-common:2.6.0-rc01")
+    docs("androidx.navigation:navigation-common-ktx:2.6.0-rc01")
+    docs("androidx.navigation:navigation-compose:2.6.0-rc01")
+    samples("androidx.navigation:navigation-compose-samples:2.6.0-rc01")
+    docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-rc01")
+    docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-rc01")
+    docs("androidx.navigation:navigation-fragment:2.6.0-rc01")
+    docs("androidx.navigation:navigation-fragment-ktx:2.6.0-rc01")
+    docs("androidx.navigation:navigation-runtime:2.6.0-rc01")
+    docs("androidx.navigation:navigation-runtime-ktx:2.6.0-rc01")
+    docs("androidx.navigation:navigation-testing:2.6.0-rc01")
+    docs("androidx.navigation:navigation-ui:2.6.0-rc01")
+    docs("androidx.navigation:navigation-ui-ktx:2.6.0-rc01")
     docs("androidx.paging:paging-common:3.2.0-alpha05")
     docs("androidx.paging:paging-common-ktx:3.2.0-alpha05")
     docs("androidx.paging:paging-compose:1.0.0-alpha19")
@@ -270,10 +271,10 @@
     docs("androidx.preference:preference:1.2.0")
     docs("androidx.preference:preference-ktx:1.2.0")
     docs("androidx.print:print:1.1.0-beta01")
-    docs("androidx.privacysandbox.ads:ads-adservices:1.0.0-beta03")
-    docs("androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta03")
-    docs("androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha03")
-    docs("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha03")
+    docs("androidx.privacysandbox.ads:ads-adservices:1.0.0-beta04")
+    docs("androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta04")
+    docs("androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha04")
+    docs("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha04")
     docs("androidx.privacysandbox.tools:tools:1.0.0-alpha03")
     docs("androidx.privacysandbox.tools:tools-apigenerator:1.0.0-alpha03")
     docs("androidx.privacysandbox.tools:tools-apipackager:1.0.0-alpha03")
@@ -343,8 +344,8 @@
     docs("androidx.tracing:tracing-ktx:1.2.0-beta04")
     docs("androidx.tracing:tracing-perfetto:1.0.0-alpha15")
     docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha15")
-    docs("androidx.transition:transition:1.4.1")
-    docs("androidx.transition:transition-ktx:1.4.1")
+    docs("androidx.transition:transition:1.5.0-alpha01")
+    docs("androidx.transition:transition-ktx:1.5.0-alpha01")
     docs("androidx.tv:tv-foundation:1.0.0-alpha06")
     docs("androidx.tv:tv-material:1.0.0-alpha06")
     samples("androidx.tv:tv-samples:1.0.0-alpha06")
@@ -355,27 +356,27 @@
     docs("androidx.versionedparcelable:versionedparcelable:1.1.1")
     docs("androidx.viewpager2:viewpager2:1.1.0-beta01")
     docs("androidx.viewpager:viewpager:1.1.0-alpha01")
-    docs("androidx.wear.compose:compose-foundation:1.2.0-alpha09")
-    samples("androidx.wear.compose:compose-foundation-samples:1.2.0-alpha09")
-    docs("androidx.wear.compose:compose-material:1.2.0-alpha09")
-    docs("androidx.wear.compose:compose-material-core:1.2.0-alpha09")
-    samples("androidx.wear.compose:compose-material-samples:1.2.0-alpha09")
-    docs("androidx.wear.compose:compose-material3:1.0.0-alpha03")
-    samples("androidx.wear.compose:compose-material3-samples:1.2.0-alpha09")
-    docs("androidx.wear.compose:compose-navigation:1.2.0-alpha09")
-    samples("androidx.wear.compose:compose-navigation-samples:1.2.0-alpha09")
-    docs("androidx.wear.compose:compose-ui-tooling:1.2.0-alpha09")
-    docs("androidx.wear.protolayout:protolayout:1.0.0-alpha08")
-    docs("androidx.wear.protolayout:protolayout-expression:1.0.0-alpha08")
-    docs("androidx.wear.protolayout:protolayout-material:1.0.0-alpha08")
-    docs("androidx.wear.protolayout:protolayout-proto:1.0.0-alpha08")
-    docs("androidx.wear.protolayout:protolayout-renderer:1.0.0-alpha08")
-    docs("androidx.wear.tiles:tiles:1.2.0-alpha04")
-    docs("androidx.wear.tiles:tiles-material:1.2.0-alpha04")
-    docs("androidx.wear.tiles:tiles-proto:1.2.0-alpha04")
-    docs("androidx.wear.tiles:tiles-renderer:1.2.0-alpha04")
-    docs("androidx.wear.tiles:tiles-testing:1.2.0-alpha04")
-    docs("androidx.wear.tiles:tiles-tooling:1.2.0-alpha04")
+    docs("androidx.wear.compose:compose-foundation:1.2.0-alpha10")
+    samples("androidx.wear.compose:compose-foundation-samples:1.2.0-alpha10")
+    docs("androidx.wear.compose:compose-material:1.2.0-alpha10")
+    docs("androidx.wear.compose:compose-material-core:1.2.0-alpha10")
+    samples("androidx.wear.compose:compose-material-samples:1.2.0-alpha10")
+    docs("androidx.wear.compose:compose-material3:1.0.0-alpha04")
+    samples("androidx.wear.compose:compose-material3-samples:1.2.0-alpha10")
+    docs("androidx.wear.compose:compose-navigation:1.2.0-alpha10")
+    samples("androidx.wear.compose:compose-navigation-samples:1.2.0-alpha10")
+    docs("androidx.wear.compose:compose-ui-tooling:1.2.0-alpha10")
+    docs("androidx.wear.protolayout:protolayout:1.0.0-alpha09")
+    docs("androidx.wear.protolayout:protolayout-expression:1.0.0-alpha09")
+    docs("androidx.wear.protolayout:protolayout-material:1.0.0-alpha09")
+    docs("androidx.wear.protolayout:protolayout-proto:1.0.0-alpha09")
+    docs("androidx.wear.protolayout:protolayout-renderer:1.0.0-alpha09")
+    docs("androidx.wear.tiles:tiles:1.2.0-alpha05")
+    docs("androidx.wear.tiles:tiles-material:1.2.0-alpha05")
+    docs("androidx.wear.tiles:tiles-proto:1.2.0-alpha05")
+    docs("androidx.wear.tiles:tiles-renderer:1.2.0-alpha05")
+    docs("androidx.wear.tiles:tiles-testing:1.2.0-alpha05")
+    docs("androidx.wear.tiles:tiles-tooling:1.2.0-alpha05")
     docs("androidx.wear.watchface:watchface:1.2.0-alpha08")
     docs("androidx.wear.watchface:watchface-client:1.2.0-alpha08")
     docs("androidx.wear.watchface:watchface-client-guava:1.2.0-alpha08")
@@ -400,17 +401,17 @@
     docs("androidx.wear:wear-input:1.2.0-alpha02")
     samples("androidx.wear:wear-input-samples:1.2.0-alpha01")
     docs("androidx.wear:wear-input-testing:1.2.0-alpha02")
-    docs("androidx.webkit:webkit:1.7.0-beta01")
+    docs("androidx.webkit:webkit:1.7.0-rc01")
     docs("androidx.window.extensions.core:core:1.0.0-rc01")
-    docs("androidx.window:window:1.1.0-beta02")
+    docs("androidx.window:window:1.1.0-rc01")
     stubs(fileTree(dir: "../window/stubs/", include: ["window-sidecar-release-0.1.0-alpha01.aar"]))
-    docs("androidx.window:window-core:1.1.0-beta02")
+    docs("androidx.window:window-core:1.1.0-rc01")
     stubs("androidx.window:window-extensions:1.0.0-alpha01")
-    docs("androidx.window:window-java:1.1.0-beta02")
-    docs("androidx.window:window-rxjava2:1.1.0-beta02")
-    docs("androidx.window:window-rxjava3:1.1.0-beta02")
-    samples("androidx.window:window-samples:1.1.0-beta02")
-    docs("androidx.window:window-testing:1.1.0-beta02")
+    docs("androidx.window:window-java:1.1.0-rc01")
+    docs("androidx.window:window-rxjava2:1.1.0-rc01")
+    docs("androidx.window:window-rxjava3:1.1.0-rc01")
+    samples("androidx.window:window-samples:1.1.0-rc01")
+    docs("androidx.window:window-testing:1.1.0-rc01")
     docs("androidx.work:work-gcm:2.8.1")
     docs("androidx.work:work-multiprocess:2.8.1")
     docs("androidx.work:work-runtime:2.8.1")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index c221e4d..e62e0d7 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -53,9 +53,10 @@
     stubs(fileTree(dir: "../camera/camera-extensions-stub", include: ["camera-extensions-stub.jar"]))
     docs(project(":camera:camera-mlkit-vision"))
     docs(project(":camera:camera-lifecycle"))
-    docs(project(":camera:camera-viewfinder"))
     docs(project(":camera:camera-video"))
     docs(project(":camera:camera-view"))
+    docs(project(":camera:camera-viewfinder"))
+    docs(project(":camera:camera-viewfinder-compose"))
     docs(project(":camera:camera-viewfinder-core"))
     docs(project(":car:app:app"))
     docs(project(":car:app:app-automotive"))
diff --git a/docs/api_guidelines/compat.md b/docs/api_guidelines/compat.md
index 147b4b4..90860c8 100644
--- a/docs/api_guidelines/compat.md
+++ b/docs/api_guidelines/compat.md
@@ -56,7 +56,13 @@
 ```
 
 When developing against pre-release SDKs where the `SDK_INT` has not been
-finalized, SDK checks **must** use `BuildCompat.isAtLeastX()` methods.
+finalized, SDK checks **must** use `BuildCompat.isAtLeastX()` methods and
+**must** use a tip-of-tree `project` dependency to ensure that the
+implementation of `BuildCompat` stays up-to-date when the SDK is finalized.
+
+**Do not** assume that the next SDK release's `SDK_INT` will be N+1. The value
+is not finalized until SDK finalization happens, at which point the `isAtLeast`
+check will be updated. **Never** write your own check for a pre-release SDK.
 
 ```java {.good}
 @NonNull
@@ -68,6 +74,12 @@
 }
 ```
 
+```kotlin {.good}
+dependencies {
+  api(project(":core:core"))
+}
+```
+
 ##### Preventing invalid casting {#compat-casting}
 
 Even when a call to a new API is moved to a version-specific class, a class
diff --git a/docs/api_guidelines/modules.md b/docs/api_guidelines/modules.md
index 85259bd..c125cc5 100644
--- a/docs/api_guidelines/modules.md
+++ b/docs/api_guidelines/modules.md
@@ -40,6 +40,45 @@
 should follow the naming convention `com.android.extensions.<feature-name>` to
 avoid placing `androidx`-packaged code in the platform's boot classpath.
 
+#### Maven name and description
+
+The `name` and `description` fields of the `androidx` configuration block are
+used to generate Maven artifact metadata, which is displayed on the artifact's
+maven.google.com entry and d.android.com landing page.
+
+```
+androidx {
+    name = "WorkManager Kotlin Extensions"
+    description = "Kotlin-friendly extensions for WorkManager."
+}
+```
+
+The name should be a human-readable, title-cased representation of the
+artifact's Maven coordinate. All components of the name **must** appear in the
+artifact's Maven group or artifact ID, with some exceptions:
+
+-   Marketing names may be shortened when used in the Maven group or artifact
+    ID, ex. "WorkManager" as `work`, "Android for Cars" as `car`, or "Kotlin
+    Extensions" as `ktx`
+-   Long (>10 character) words may be truncated to a short (>5 character) prefix
+-   Pluralization may be changed, ex. "Views" as `view`
+-   The following descriptive terms may appear in the name:
+    -   "extension(s)"
+    -   "for"
+    -   "integration"
+    -   "with"
+
+**Do not** use the following terms in the name:
+
+-   "AndroidX"
+-   "Library"
+-   "Implementation"
+
+The description should be a single phrase that completes the sentence, "This
+library provides ...". This phrase should provide enough description that a
+developer can decide whether they might want to learn more about using your
+library. **Do not** simply repeat the name of the library.
+
 #### Project directory structure {#module-structure}
 
 Libraries developed in AndroidX follow a consistent project naming and directory
@@ -284,7 +323,7 @@
 ...
 
 androidx {
-    name = "Android Support Library collections"
+    name = "Collection"
     type = LibraryType.KMP_LIBRARY
     mavenGroup = LibraryGroups.COLLECTION
     mavenVersion = KmpPlatformsKt.enableNative(project) ? LibraryVersions.COLLECTION_KMP : LibraryVersions.KMP
diff --git a/documentfile/documentfile/src/main/AndroidManifest.xml b/documentfile/documentfile/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/documentfile/documentfile/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/drawerlayout/drawerlayout/src/main/AndroidManifest.xml b/drawerlayout/drawerlayout/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/drawerlayout/drawerlayout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/dynamicanimation/dynamicanimation-ktx/src/main/AndroidManifest.xml b/dynamicanimation/dynamicanimation-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 9db011d..0000000
--- a/dynamicanimation/dynamicanimation-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/dynamicanimation/dynamicanimation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java b/dynamicanimation/dynamicanimation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java
index 58b53c0..7caf3e7 100644
--- a/dynamicanimation/dynamicanimation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java
+++ b/dynamicanimation/dynamicanimation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java
@@ -126,6 +126,7 @@
      * Test that spring animation can work with a single property without an object.
      */
     @Test
+    @Ignore("b/280665072")
     public void testFloatValueHolder() {
         final FloatValueHolder floatValueHolder = new FloatValueHolder(0f);
         DynamicAnimation.OnAnimationUpdateListener updateListener =
@@ -354,6 +355,7 @@
      */
     @LargeTest
     @Test
+    @Ignore("b/280665072")
     public void testStiffness() {
         float[] dampingRatios = {0.3f, 0.5f, 1f, 5f};
         final float[] stiffness = {50f, 500f, 1500f, 5000f};
diff --git a/dynamicanimation/dynamicanimation/src/main/AndroidManifest.xml b/dynamicanimation/dynamicanimation/src/main/AndroidManifest.xml
deleted file mode 100644
index 95c4426..0000000
--- a/dynamicanimation/dynamicanimation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/emoji/emoji-appcompat/src/main/AndroidManifest.xml b/emoji/emoji-appcompat/src/main/AndroidManifest.xml
deleted file mode 100644
index c7003d8..0000000
--- a/emoji/emoji-appcompat/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/emoji/emoji-bundled/src/main/AndroidManifest.xml b/emoji/emoji-bundled/src/main/AndroidManifest.xml
deleted file mode 100644
index c50336e..0000000
--- a/emoji/emoji-bundled/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
\ No newline at end of file
diff --git a/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java b/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java
index e75969a..5130a65 100644
--- a/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java
+++ b/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java
@@ -30,13 +30,19 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.widget.EditText;
 import android.widget.TextView;
 
 import androidx.emoji.text.EmojiCompat;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -126,4 +132,31 @@
         verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
         verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void initCallback_doesntCrashWhenNotAttached() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        EditText editText = new EditText(context);
+        EmojiInputFilter subject = new EmojiInputFilter(editText);
+        subject.getInitCallback().onInitialized();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29)
+    public void initCallback_sendsToNonMainHandler_beforeSetText() {
+        // this is just testing that onInitialized dispatches to editText.getHandler before setText
+        EditText mockEditText = mock(EditText.class);
+        HandlerThread thread = new HandlerThread("random thread");
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        thread.quitSafely();
+        when(mockEditText.getHandler()).thenReturn(handler);
+        EmojiInputFilter subject = new EmojiInputFilter(mockEditText);
+        EmojiInputFilter.InitCallbackImpl initCallback =
+                (EmojiInputFilter.InitCallbackImpl) subject.getInitCallback();
+        initCallback.onInitialized();
+
+        handler.hasCallbacks(initCallback);
+    }
 }
diff --git a/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java b/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java
index fe3da64..50b0169 100644
--- a/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java
+++ b/emoji/emoji/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java
@@ -27,13 +27,18 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.widget.EditText;
 
 import androidx.emoji.text.EmojiCompat;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -41,6 +46,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
 public class EmojiTextWatcherTest {
 
     private EmojiTextWatcher mTextWatcher;
@@ -120,4 +126,31 @@
         verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
         verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
     }
+
+    @Test
+    public void initCallback_doesntCrashWhenNotAttached() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        EditText editText = new EditText(context);
+        EmojiTextWatcher subject = new EmojiTextWatcher(editText);
+        subject.getInitCallback().onInitialized();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29)
+    public void initCallback_sendsToNonMainHandler_beforeSetText() {
+        // this is just testing that onInitialized dispatches to editText.getHandler before setText
+        EditText mockEditText = mock(EditText.class);
+        HandlerThread thread = new HandlerThread("random thread");
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        thread.quitSafely();
+        when(mockEditText.getHandler()).thenReturn(handler);
+        EmojiTextWatcher subject = new EmojiTextWatcher(mockEditText);
+        EmojiTextWatcher.InitCallbackImpl initCallback =
+                (EmojiTextWatcher.InitCallbackImpl) subject.getInitCallback();
+        initCallback.onInitialized();
+
+        handler.hasCallbacks(initCallback);
+    }
 }
+
diff --git a/emoji/emoji/src/main/AndroidManifest.xml b/emoji/emoji/src/main/AndroidManifest.xml
deleted file mode 100644
index a213cdc..0000000
--- a/emoji/emoji/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest/>
diff --git a/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiInputFilter.java b/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiInputFilter.java
index 27d1aa0..25f9478 100644
--- a/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiInputFilter.java
+++ b/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiInputFilter.java
@@ -15,8 +15,10 @@
  */
 package androidx.emoji.widget;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
+import android.os.Handler;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.Spanned;
@@ -88,14 +90,16 @@
         }
     }
 
-    private InitCallback getInitCallback() {
+    @RestrictTo(LIBRARY)
+    InitCallback getInitCallback() {
         if (mInitCallback == null) {
             mInitCallback = new InitCallbackImpl(mTextView);
         }
         return mInitCallback;
     }
 
-    private static class InitCallbackImpl extends InitCallback {
+    @RestrictTo(LIBRARY)
+    static class InitCallbackImpl extends InitCallback implements Runnable {
         private final Reference<TextView> mViewRef;
 
         InitCallbackImpl(TextView textView) {
@@ -106,7 +110,23 @@
         public void onInitialized() {
             super.onInitialized();
             final TextView textView = mViewRef.get();
-            if (textView != null && textView.isAttachedToWindow()) {
+            if (textView == null) {
+                return;
+            }
+            // we need to move to the actual thread this view is using as main
+            Handler handler = textView.getHandler();
+            if (handler != null) {
+                handler.post(this);
+            }
+        }
+
+        @Override
+        public void run() {
+            final TextView textView = mViewRef.get();
+            if (textView == null) {
+                return;
+            }
+            if (textView.isAttachedToWindow()) {
                 final CharSequence result = EmojiCompat.get().process(textView.getText());
 
                 final int selectionStart = Selection.getSelectionStart(result);
diff --git a/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java b/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java
index 2a4572f..6e6fcf3 100644
--- a/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java
+++ b/emoji/emoji/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java
@@ -15,8 +15,10 @@
  */
 package androidx.emoji.widget;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
+import android.os.Handler;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.Spannable;
@@ -99,14 +101,16 @@
         // do nothing
     }
 
-    private InitCallback getInitCallback() {
+    @RestrictTo(LIBRARY)
+    InitCallback getInitCallback() {
         if (mInitCallback == null) {
             mInitCallback = new InitCallbackImpl(mEditText);
         }
         return mInitCallback;
     }
 
-    private static class InitCallbackImpl extends InitCallback {
+    @RestrictTo(LIBRARY)
+    static class InitCallbackImpl extends InitCallback implements Runnable {
         private final Reference<EditText> mViewRef;
 
         InitCallbackImpl(EditText editText) {
@@ -117,6 +121,19 @@
         public void onInitialized() {
             super.onInitialized();
             final EditText editText = mViewRef.get();
+            if (editText == null) {
+                return;
+            }
+            Handler handler = editText.getHandler();
+            if (handler == null) {
+                return;
+            }
+            handler.post(this);
+        }
+
+        @Override
+        public void run() {
+            final EditText editText = mViewRef.get();
             if (editText != null && editText.isAttachedToWindow()) {
                 final Editable text = editText.getEditableText();
 
diff --git a/emoji2/emoji2-benchmark/src/main/AndroidManifest.xml b/emoji2/emoji2-benchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 29b496b..0000000
--- a/emoji2/emoji2-benchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiInputFilterTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiInputFilterTest.java
index 3349e6d..a21adf7 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiInputFilterTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiInputFilterTest.java
@@ -30,9 +30,13 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.text.InputFilter;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.widget.EditText;
 import android.widget.TextView;
 
 import androidx.emoji2.text.EmojiCompat;
@@ -40,6 +44,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -191,7 +196,7 @@
 
         when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
         // trigger initialized
-        captor.getValue().onInitialized();
+        ((Runnable) captor.getValue()).run();
 
         verify(mEmojiCompat).process(eq(testString));
     }
@@ -223,7 +228,7 @@
         when(mEmojiCompat.process(eq(testString))).thenReturn(testString);
         when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
         // trigger initialized
-        captor.getValue().onInitialized();
+        ((Runnable) captor.getValue()).run();
 
         // validate interactions don't do anything except check for update
         verify(mTextView).getFilters();
@@ -233,4 +238,31 @@
         // if you add a safe interaction please update test
         verifyNoMoreInteractions(mTextView);
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void initCallback_doesntCrashWhenNotAttached() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        EditText editText = new EditText(context);
+        EmojiInputFilter subject = new EmojiInputFilter(editText);
+        subject.getInitCallback().onInitialized();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29)
+    public void initCallback_sendsToNonMainHandler_beforeSetText() {
+        // this is just testing that onInitialized dispatches to editText.getHandler before setText
+        EditText mockEditText = mock(EditText.class);
+        HandlerThread thread = new HandlerThread("random thread");
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        thread.quitSafely();
+        when(mockEditText.getHandler()).thenReturn(handler);
+        EmojiInputFilter subject = new EmojiInputFilter(mockEditText);
+        EmojiInputFilter.InitCallbackImpl initCallback =
+                (EmojiInputFilter.InitCallbackImpl) subject.getInitCallback();
+        initCallback.onInitialized();
+
+        handler.hasCallbacks(initCallback);
+    }
 }
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiTextWatcherTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiTextWatcherTest.java
index 8f149da..5c3c5de 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiTextWatcherTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewsintegration/EmojiTextWatcherTest.java
@@ -27,6 +27,9 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -37,6 +40,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -149,4 +153,30 @@
         mTextWatcher.onTextChanged(expected, 0, 0, 1);
         assertTrue(TextUtils.equals(expected, "abc"));
     }
+
+    @Test
+    public void initCallback_doesntCrashWhenNotAttached() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        EditText editText = new EditText(context);
+        EmojiTextWatcher subject = new EmojiTextWatcher(editText, false);
+        subject.getInitCallback().onInitialized();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29)
+    public void initCallback_sendsToNonMainHandler_beforeSetText() {
+        // this is just testing that onInitialized dispatches to editText.getHandler before setText
+        EditText mockEditText = mock(EditText.class);
+        HandlerThread thread = new HandlerThread("random thread");
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        thread.quitSafely();
+        when(mockEditText.getHandler()).thenReturn(handler);
+        EmojiTextWatcher subject = new EmojiTextWatcher(mockEditText, false);
+        EmojiTextWatcher.InitCallbackImpl initCallback =
+                (EmojiTextWatcher.InitCallbackImpl) subject.getInitCallback();
+        initCallback.onInitialized();
+
+        handler.hasCallbacks(initCallback);
+    }
 }
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiInputFilter.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiInputFilter.java
index 530fc52..9c9b485 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiInputFilter.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiInputFilter.java
@@ -15,6 +15,9 @@
  */
 package androidx.emoji2.viewsintegration;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.os.Handler;
 import android.text.InputFilter;
 import android.text.Selection;
 import android.text.Spannable;
@@ -39,7 +42,7 @@
  * effects.
  *
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RestrictTo(LIBRARY)
 @RequiresApi(19)
 final class EmojiInputFilter implements android.text.InputFilter {
     private final TextView mTextView;
@@ -88,15 +91,17 @@
         }
     }
 
-    private InitCallback getInitCallback() {
+    @RestrictTo(LIBRARY)
+    InitCallback getInitCallback() {
         if (mInitCallback == null) {
             mInitCallback = new InitCallbackImpl(mTextView, this);
         }
         return mInitCallback;
     }
 
+    @RestrictTo(LIBRARY)
     @RequiresApi(19)
-    private static class InitCallbackImpl extends InitCallback {
+    static class InitCallbackImpl extends InitCallback implements Runnable  {
         private final Reference<TextView> mViewRef;
         private final Reference<EmojiInputFilter> mEmojiInputFilterReference;
 
@@ -109,6 +114,19 @@
         @Override
         public void onInitialized() {
             super.onInitialized();
+            final TextView textView = mViewRef.get();
+            if (textView == null) {
+                return;
+            }
+            // we need to move to the actual thread this view is using as main
+            Handler handler = textView.getHandler();
+            if (handler != null) {
+                handler.post(this);
+            }
+        }
+
+        @Override
+        public void run() {
             @Nullable final TextView textView = mViewRef.get();
             @Nullable final InputFilter myInputFilter = mEmojiInputFilterReference.get();
             if (!isInputFilterCurrentlyRegisteredOnTextView(textView, myInputFilter)) return;
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiTextWatcher.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiTextWatcher.java
index 55c37da..691f995 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiTextWatcher.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewsintegration/EmojiTextWatcher.java
@@ -15,6 +15,9 @@
  */
 package androidx.emoji2.viewsintegration;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.os.Handler;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.Spannable;
@@ -34,7 +37,7 @@
  * TextWatcher used for an EditText.
  *
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RestrictTo(LIBRARY)
 @RequiresApi(19)
 final class EmojiTextWatcher implements android.text.TextWatcher {
     private final EditText mEditText;
@@ -107,7 +110,11 @@
         // do nothing
     }
 
-    private InitCallback getInitCallback() {
+    /**
+     * @return
+     */
+    @RestrictTo(LIBRARY)
+    InitCallback getInitCallback() {
         if (mInitCallback == null) {
             mInitCallback = new InitCallbackImpl(mEditText);
         }
@@ -130,8 +137,9 @@
         }
     }
 
+    @RestrictTo(LIBRARY)
     @RequiresApi(19)
-    private static class InitCallbackImpl extends InitCallback {
+    static class InitCallbackImpl extends InitCallback implements Runnable {
         private final Reference<EditText> mViewRef;
 
         InitCallbackImpl(EditText editText) {
@@ -142,6 +150,19 @@
         public void onInitialized() {
             super.onInitialized();
             final EditText editText = mViewRef.get();
+            if (editText == null) {
+                return;
+            }
+            Handler handler = editText.getHandler();
+            if (handler == null) {
+                return;
+            }
+            handler.post(this);
+        }
+
+        @Override
+        public void run() {
+            final EditText editText = mViewRef.get();
             processTextOnEnablingEvent(editText, EmojiCompat.LOAD_STATE_SUCCEEDED);
         }
     }
diff --git a/emoji2/integration-tests/init-disabled-macrobenchmark/src/androidTest/AndroidManifest.xml b/emoji2/integration-tests/init-disabled-macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/emoji2/integration-tests/init-disabled-macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/emoji2/integration-tests/init-disabled-macrobenchmark/src/main/AndroidManifest.xml b/emoji2/integration-tests/init-disabled-macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index ed173d5..0000000
--- a/emoji2/integration-tests/init-disabled-macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark/src/androidTest/AndroidManifest.xml b/emoji2/integration-tests/init-enabled-macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/emoji2/integration-tests/init-enabled-macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark/src/main/AndroidManifest.xml b/emoji2/integration-tests/init-enabled-macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index ed173d5..0000000
--- a/emoji2/integration-tests/init-enabled-macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/enterprise/enterprise-feedback-testing/src/main/AndroidManifest.xml b/enterprise/enterprise-feedback-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index de53569..0000000
--- a/enterprise/enterprise-feedback-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/exifinterface/exifinterface/src/main/AndroidManifest.xml b/exifinterface/exifinterface/src/main/AndroidManifest.xml
deleted file mode 100644
index 5197378..0000000
--- a/exifinterface/exifinterface/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/external/libyuv/src/main/AndroidManifest.xml b/external/libyuv/src/main/AndroidManifest.xml
deleted file mode 100644
index 95ab252..0000000
--- a/external/libyuv/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
diff --git a/fragment/fragment-ktx/src/main/AndroidManifest.xml b/fragment/fragment-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/fragment/fragment-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/fragment/fragment-truth/src/main/AndroidManifest.xml b/fragment/fragment-truth/src/main/AndroidManifest.xml
deleted file mode 100644
index cf0d807..0000000
--- a/fragment/fragment-truth/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/fragment/fragment/src/main/AndroidManifest.xml b/fragment/fragment/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/fragment/fragment/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ProgressIndicatorAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ProgressIndicatorAppWidget.kt
index 3f5283a..171a220 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ProgressIndicatorAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ProgressIndicatorAppWidget.kt
@@ -17,22 +17,30 @@
 package androidx.glance.appwidget.demos
 
 import android.content.Context
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import androidx.glance.GlanceId
 import androidx.glance.GlanceModifier
+import androidx.glance.GlanceTheme
 import androidx.glance.appwidget.CircularProgressIndicator
 import androidx.glance.appwidget.GlanceAppWidget
 import androidx.glance.appwidget.GlanceAppWidgetReceiver
 import androidx.glance.appwidget.LinearProgressIndicator
 import androidx.glance.appwidget.provideContent
 import androidx.glance.background
+import androidx.glance.color.ColorProvider
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.Column
+import androidx.glance.layout.Row
 import androidx.glance.layout.Spacer
 import androidx.glance.layout.fillMaxSize
 import androidx.glance.layout.padding
 import androidx.glance.layout.size
+import androidx.glance.layout.width
+import androidx.glance.material3.ColorProviders
 import androidx.glance.unit.ColorProvider
 
 class ProgressIndicatorAppWidget : GlanceAppWidget() {
@@ -40,31 +48,97 @@
         context: Context,
         id: GlanceId
     ) = provideContent {
-        Column(
-            modifier = GlanceModifier.fillMaxSize()
-                .background(R.color.default_widget_background),
-            verticalAlignment = Alignment.CenterVertically,
-            horizontalAlignment = Alignment.CenterHorizontally
-        ) {
+        GlanceTheme(ProgressIndicatorDemoColorScheme.colors) {
+            Column(
+                modifier = GlanceModifier.fillMaxSize()
+                    .background(R.color.default_widget_background),
+                verticalAlignment = Alignment.CenterVertically,
+                horizontalAlignment = Alignment.CenterHorizontally
+            ) {
+                Row {
+                    LinearProgressIndicatorsDemo(modifier = GlanceModifier.defaultWeight())
+                    Spacer(modifier = GlanceModifier.width(10.dp))
+                    CircularProgressIndicatorsDemo()
+                }
+            }
+        }
+    }
+
+    @Composable
+    private fun CircularProgressIndicatorsDemo(modifier: GlanceModifier = GlanceModifier) {
+        Column(modifier) {
+            CircularProgressIndicator()
+            Spacer(GlanceModifier.size(8.dp))
+            CircularProgressIndicator(
+                color = GlanceTheme.colors.primary
+            )
+            Spacer(GlanceModifier.size(8.dp))
+            CircularProgressIndicator(
+                color = ColorProvider(day = Color.White, night = Color.Red)
+            )
+            Spacer(GlanceModifier.size(8.dp))
+            CircularProgressIndicator(
+                color = ColorProvider(androidx.glance.R.color.glance_colorSecondary)
+            )
+            Spacer(GlanceModifier.size(8.dp))
+            CircularProgressIndicator(
+                color = ColorProvider(Color.Red)
+            )
+        }
+    }
+
+    @Composable
+    private fun LinearProgressIndicatorsDemo(modifier: GlanceModifier) {
+        Column(modifier) {
             LinearProgressIndicator()
             Spacer(GlanceModifier.size(8.dp))
-            LinearProgressIndicator(0.5f)
+            LinearProgressIndicator(
+                progress = 0.5f,
+                color = GlanceTheme.colors.primary,
+                backgroundColor = GlanceTheme.colors.onBackground
+            )
             Spacer(GlanceModifier.size(8.dp))
             LinearProgressIndicator(
                 progress = .66f,
                 modifier = GlanceModifier.padding(bottom = 8.dp),
                 color = ColorProvider(androidx.glance.R.color.glance_colorError),
-                backgroundColor = ColorProvider(androidx.glance.R.color.glance_colorSecondary)
+                backgroundColor = ColorProvider(
+                    resId = androidx.glance.R.color.glance_colorSecondary
+                )
             )
+            Spacer(GlanceModifier.size(8.dp))
+            LinearProgressIndicator(
+                progress = .66f,
+                modifier = GlanceModifier.padding(bottom = 8.dp),
+                color = ColorProvider(day = Color.White, night = Color.Red),
+                backgroundColor = ColorProvider(day = Color.Red, night = Color.White)
+            )
+            Spacer(GlanceModifier.size(8.dp))
             LinearProgressIndicator(progress = 0.8f, color = ColorProvider(Color.White))
-            Spacer(GlanceModifier.size(8.dp))
-            CircularProgressIndicator()
-            Spacer(GlanceModifier.size(8.dp))
-            CircularProgressIndicator(color = ColorProvider(Color.White))
         }
     }
 }
 
 class ProgressIndicatorAppWidgetReceiver : GlanceAppWidgetReceiver() {
     override val glanceAppWidget: GlanceAppWidget = ProgressIndicatorAppWidget()
+}
+
+internal object ProgressIndicatorDemoColorScheme {
+    private val md_theme_light_primary = Color(0xFF026E00)
+    private val md_theme_light_onBackground = Color(0xFF1E1C00)
+
+    private val md_theme_dark_primary = Color(0xFF02E600)
+    private val md_theme_dark_onBackground = Color(0xFFF2E720)
+
+    private val LightColors = lightColorScheme(
+        primary = md_theme_light_primary,
+        onBackground = md_theme_light_onBackground,
+    )
+
+    private val DarkColors = darkColorScheme(
+        primary = md_theme_dark_primary,
+        onBackground = md_theme_dark_onBackground,
+    )
+
+    val colors = ColorProviders(light = LightColors, dark = DarkColors)
 }
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CircularProgressIndicatorTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CircularProgressIndicatorTranslator.kt
index 0d7c717..33a20ed 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CircularProgressIndicatorTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/CircularProgressIndicatorTranslator.kt
@@ -25,10 +25,13 @@
 import androidx.core.widget.RemoteViewsCompat.setProgressBarIndeterminateTintList
 import androidx.compose.ui.graphics.toArgb
 import android.content.res.ColorStateList
+import android.util.Log
 import androidx.glance.unit.FixedColorProvider
 import androidx.glance.unit.ResourceColorProvider
 
 import androidx.glance.appwidget.EmittableCircularProgressIndicator
+import androidx.glance.appwidget.GlanceAppWidgetTag
+import androidx.glance.color.DayNightColorProvider
 
 internal fun RemoteViews.translateEmittableCircularProgressIndicator(
     translationContext: TranslationContext,
@@ -49,9 +52,18 @@
         is ResourceColorProvider -> {
           setProgressBarIndeterminateTintList(
             viewId = viewDef.mainViewId,
-            tint = ColorStateList.valueOf(indicatorColor.resId)
+            resId = indicatorColor.resId
           )
         }
+        is DayNightColorProvider -> {
+          setProgressBarIndeterminateTintList(
+              viewId = viewDef.mainViewId,
+              notNightTint = ColorStateList.valueOf(indicatorColor.day.toArgb()),
+              nightTint = ColorStateList.valueOf(indicatorColor.night.toArgb())
+          )
+        }
+        else ->
+            Log.w(GlanceAppWidgetTag, "Unexpected progress indicator color: $indicatorColor")
       }
     }
     applyModifiers(translationContext, this, element.modifier, viewDef)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/LinearProgressIndicatorTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/LinearProgressIndicatorTranslator.kt
index 0c1342b..5b9aca4 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/LinearProgressIndicatorTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/LinearProgressIndicatorTranslator.kt
@@ -26,10 +26,13 @@
 import androidx.core.widget.RemoteViewsCompat.setProgressBarProgressBackgroundTintList
 import androidx.compose.ui.graphics.toArgb
 import android.content.res.ColorStateList
+import android.util.Log
 import androidx.glance.unit.FixedColorProvider
 import androidx.glance.unit.ResourceColorProvider
 
 import androidx.glance.appwidget.EmittableLinearProgressIndicator
+import androidx.glance.appwidget.GlanceAppWidgetTag
+import androidx.glance.color.DayNightColorProvider
 
 internal fun RemoteViews.translateEmittableLinearProgressIndicator(
     translationContext: TranslationContext,
@@ -54,6 +57,15 @@
             resId = indicatorColor.resId
           )
         }
+        is DayNightColorProvider -> {
+            setProgressBarProgressTintList(
+                viewId = viewDef.mainViewId,
+                notNightTint = ColorStateList.valueOf(indicatorColor.day.toArgb()),
+                nightTint = ColorStateList.valueOf(indicatorColor.night.toArgb())
+            )
+        }
+        else ->
+            Log.w(GlanceAppWidgetTag, "Unexpected progress indicator color: $indicatorColor")
       }
 
       when (val backgroundColor = element.backgroundColor) {
@@ -69,6 +81,16 @@
             resId = backgroundColor.resId
           )
         }
+        is DayNightColorProvider -> {
+          setProgressBarProgressBackgroundTintList(
+            viewId = viewDef.mainViewId,
+            notNightTint = ColorStateList.valueOf(backgroundColor.day.toArgb()),
+            nightTint = ColorStateList.valueOf(backgroundColor.night.toArgb())
+          )
+        }
+        else ->
+            Log.w(GlanceAppWidgetTag,
+                "Unexpected progress indicator background color: $backgroundColor")
       }
     }
     applyModifiers(translationContext, this, element.modifier, viewDef)
diff --git a/glance/glance-wear-tiles/src/androidAndroidTest/AndroidManifest.xml b/glance/glance-wear-tiles/src/androidAndroidTest/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/glance/glance-wear-tiles/src/androidAndroidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/glance/glance-wear-tiles/src/androidMain/AndroidManifest.xml b/glance/glance-wear-tiles/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/glance/glance-wear-tiles/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/glance/glance/src/androidMain/AndroidManifest.xml b/glance/glance/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/glance/glance/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
index ba81a87..b2c764d 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
@@ -753,7 +753,9 @@
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
     fun testUsageFlagContainsFrontBufferUsage() {
         val usageFlags = FrontBufferUtils.obtainHardwareBufferUsageFlags()
-        if (UsageFlagsVerificationHelper.isSupported(HardwareBuffer.USAGE_FRONT_BUFFER)) {
+        // See b/280866371
+        if (UsageFlagsVerificationHelper.isSupported(HardwareBuffer.USAGE_FRONT_BUFFER) &&
+            !Build.MODEL.contains("Cuttlefish")) {
             assertNotEquals(0, usageFlags and HardwareBuffer.USAGE_FRONT_BUFFER)
         } else {
             assertEquals(0, usageFlags and HardwareBuffer.USAGE_FRONT_BUFFER)
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt
index 5b00170..7321a55 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt
@@ -93,7 +93,11 @@
         fun obtainUsageFlagsV33(): Long {
             // First verify if the front buffer usage flag is supported along with the
             // "usage composer overlay" flag that was introduced in API level 33
-            return if (isSupported(HardwareBuffer.USAGE_FRONT_BUFFER)) {
+            // SF Seems to log errors when configuring HardwareBuffer instances with the
+            // front buffer usage flag on Cuttlefish, so only include it for actual devices.
+            // See b/280866371
+            return if (isSupported(HardwareBuffer.USAGE_FRONT_BUFFER) &&
+                !Build.MODEL.contains("Cuttlefish")) {
                 FrontBufferUtils.BaseFlags or HardwareBuffer.USAGE_FRONT_BUFFER
             } else {
                 FrontBufferUtils.BaseFlags
diff --git a/gridlayout/gridlayout/api/1.1.0-beta01.txt b/gridlayout/gridlayout/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..abbe71f
--- /dev/null
+++ b/gridlayout/gridlayout/api/1.1.0-beta01.txt
@@ -0,0 +1,71 @@
+// Signature format: 4.0
+package androidx.gridlayout.widget {
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public GridLayout(android.content.Context!, android.util.AttributeSet!);
+    ctor public GridLayout(android.content.Context!);
+    method protected androidx.gridlayout.widget.GridLayout.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.gridlayout.widget.GridLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.gridlayout.widget.GridLayout.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer! getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer!);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, androidx.gridlayout.widget.GridLayout.Alignment!, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, androidx.gridlayout.widget.GridLayout.Alignment!, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, androidx.gridlayout.widget.GridLayout.Alignment!);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, androidx.gridlayout.widget.GridLayout.Alignment!);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! BASELINE;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! BOTTOM;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! CENTER;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! END;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! LEFT;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! RIGHT;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! START;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public abstract static class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(androidx.gridlayout.widget.GridLayout.Spec!, androidx.gridlayout.widget.GridLayout.Spec!);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+    ctor public GridLayout.LayoutParams(androidx.gridlayout.widget.GridLayout.LayoutParams!);
+    ctor public GridLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    method public void setGravity(int);
+    field public androidx.gridlayout.widget.GridLayout.Spec! columnSpec;
+    field public androidx.gridlayout.widget.GridLayout.Spec! rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public androidx.gridlayout.widget.GridLayout.Alignment! getAbsoluteAlignment(boolean);
+  }
+
+}
+
diff --git a/gridlayout/gridlayout/api/public_plus_experimental_1.1.0-beta01.txt b/gridlayout/gridlayout/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..abbe71f
--- /dev/null
+++ b/gridlayout/gridlayout/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,71 @@
+// Signature format: 4.0
+package androidx.gridlayout.widget {
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public GridLayout(android.content.Context!, android.util.AttributeSet!);
+    ctor public GridLayout(android.content.Context!);
+    method protected androidx.gridlayout.widget.GridLayout.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.gridlayout.widget.GridLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.gridlayout.widget.GridLayout.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer! getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer!);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, androidx.gridlayout.widget.GridLayout.Alignment!, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, androidx.gridlayout.widget.GridLayout.Alignment!, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, androidx.gridlayout.widget.GridLayout.Alignment!);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, androidx.gridlayout.widget.GridLayout.Alignment!);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! BASELINE;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! BOTTOM;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! CENTER;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! END;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! LEFT;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! RIGHT;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! START;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public abstract static class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(androidx.gridlayout.widget.GridLayout.Spec!, androidx.gridlayout.widget.GridLayout.Spec!);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+    ctor public GridLayout.LayoutParams(androidx.gridlayout.widget.GridLayout.LayoutParams!);
+    ctor public GridLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    method public void setGravity(int);
+    field public androidx.gridlayout.widget.GridLayout.Spec! columnSpec;
+    field public androidx.gridlayout.widget.GridLayout.Spec! rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public androidx.gridlayout.widget.GridLayout.Alignment! getAbsoluteAlignment(boolean);
+  }
+
+}
+
diff --git a/gridlayout/gridlayout/api/res-1.1.0-beta01.txt b/gridlayout/gridlayout/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gridlayout/gridlayout/api/res-1.1.0-beta01.txt
diff --git a/gridlayout/gridlayout/api/restricted_1.1.0-beta01.txt b/gridlayout/gridlayout/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..abbe71f
--- /dev/null
+++ b/gridlayout/gridlayout/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,71 @@
+// Signature format: 4.0
+package androidx.gridlayout.widget {
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public GridLayout(android.content.Context!, android.util.AttributeSet!);
+    ctor public GridLayout(android.content.Context!);
+    method protected androidx.gridlayout.widget.GridLayout.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.gridlayout.widget.GridLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.gridlayout.widget.GridLayout.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer! getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer!);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, androidx.gridlayout.widget.GridLayout.Alignment!, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, androidx.gridlayout.widget.GridLayout.Alignment!, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, float);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int, androidx.gridlayout.widget.GridLayout.Alignment!);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, androidx.gridlayout.widget.GridLayout.Alignment!);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int, int);
+    method public static androidx.gridlayout.widget.GridLayout.Spec! spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! BASELINE;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! BOTTOM;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! CENTER;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! END;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! LEFT;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! RIGHT;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! START;
+    field public static final androidx.gridlayout.widget.GridLayout.Alignment! TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public abstract static class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(androidx.gridlayout.widget.GridLayout.Spec!, androidx.gridlayout.widget.GridLayout.Spec!);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+    ctor public GridLayout.LayoutParams(androidx.gridlayout.widget.GridLayout.LayoutParams!);
+    ctor public GridLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    method public void setGravity(int);
+    field public androidx.gridlayout.widget.GridLayout.Spec! columnSpec;
+    field public androidx.gridlayout.widget.GridLayout.Spec! rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public androidx.gridlayout.widget.GridLayout.Alignment! getAbsoluteAlignment(boolean);
+  }
+
+}
+
diff --git a/gridlayout/gridlayout/src/main/AndroidManifest.xml b/gridlayout/gridlayout/src/main/AndroidManifest.xml
deleted file mode 100644
index bd7af07..0000000
--- a/gridlayout/gridlayout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/health/connect/connect-client/samples/src/main/AndroidManifest.xml b/health/connect/connect-client/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 676b645..0000000
--- a/health/connect/connect-client/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/main/AndroidManifest.xml b/heifwriter/heifwriter/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/heifwriter/heifwriter/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/hilt/hilt-navigation-compose/samples/src/main/AndroidManifest.xml b/hilt/hilt-navigation-compose/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 20af317..0000000
--- a/hilt/hilt-navigation-compose/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/hilt/hilt-navigation-compose/src/main/AndroidManifest.xml b/hilt/hilt-navigation-compose/src/main/AndroidManifest.xml
deleted file mode 100644
index 38e6839..0000000
--- a/hilt/hilt-navigation-compose/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/hilt/hilt-navigation-fragment/src/main/AndroidManifest.xml b/hilt/hilt-navigation-fragment/src/main/AndroidManifest.xml
deleted file mode 100644
index 38e6839..0000000
--- a/hilt/hilt-navigation-fragment/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/hilt/hilt-navigation/src/main/AndroidManifest.xml b/hilt/hilt-navigation/src/main/AndroidManifest.xml
deleted file mode 100644
index 38e6839..0000000
--- a/hilt/hilt-navigation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/input/input-motionprediction/src/main/AndroidManifest.xml b/input/input-motionprediction/src/main/AndroidManifest.xml
deleted file mode 100644
index e4e6dc1f..0000000
--- a/input/input-motionprediction/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2022 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
\ No newline at end of file
diff --git a/inspection/inspection-testing/src/main/AndroidManifest.xml b/inspection/inspection-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/inspection/inspection-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/inspection/inspection/src/main/AndroidManifest.xml b/inspection/inspection/src/main/AndroidManifest.xml
deleted file mode 100644
index 873a38a..0000000
--- a/inspection/inspection/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-    <!-- Copyright (C) 2014 The Android Open Source Project
-
-         Licensed under the Apache License, Version 2.0 (the "License");
-         you may not use this file except in compliance with the License.
-         You may obtain a copy of the License at
-
-              http://www.apache.org/licenses/LICENSE-2.0
-
-         Unless required by applicable law or agreed to in writing, software
-         distributed under the License is distributed on an "AS IS" BASIS,
-         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-         See the License for the specific language governing permissions and
-         limitations under the License.
-    -->
-<manifest />
diff --git a/interpolator/interpolator/src/main/AndroidManifest.xml b/interpolator/interpolator/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/interpolator/interpolator/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
index 495d89f..1619734 100644
--- a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
+++ b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
@@ -33,6 +33,7 @@
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -208,6 +209,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testInfiniteLoop() throws Throwable {
         final String code = "while(true){}";
         Context context = ApplicationProvider.getApplicationContext();
@@ -236,6 +238,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testMultipleInfiniteLoops() throws Throwable {
         final String code = "while(true){}";
         final int num_of_evaluations = 10;
@@ -270,6 +273,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testSimpleArrayBuffer() throws Throwable {
         final String provideString = "Hello World";
         final byte[] bytes = provideString.getBytes(StandardCharsets.US_ASCII);
@@ -301,6 +305,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testArrayBufferWasmCompilation() throws Throwable {
         final String success = "success";
         // The bytes of a minimal WebAssembly module, courtesy of v8/test/cctest/test-api-wasm.cc
@@ -334,6 +339,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testPromiseReturn() throws Throwable {
         final String code = "Promise.resolve(\"PASS\")";
         final String expected = "PASS";
@@ -354,6 +360,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testPromiseReturnLaterResolve() throws Throwable {
         final String code1 = "var promiseResolve, promiseReject;"
                 + "new Promise(function(resolve, reject){"
@@ -381,6 +388,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testNestedConsumeNamedDataAsArrayBuffer() throws Throwable {
         final String success = "success";
         // The bytes of a minimal WebAssembly module, courtesy of v8/test/cctest/test-api-wasm.cc
@@ -424,6 +432,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testPromiseEvaluationThrow() throws Throwable {
         final String provideString = "Hello World";
         final byte[] bytes = provideString.getBytes(StandardCharsets.US_ASCII);
@@ -546,6 +555,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testHeapSizeAdjustment() throws Throwable {
         final String code = "\"PASS\"";
         final String expected = "PASS";
@@ -585,6 +595,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testHeapSizeEnforced() throws Throwable {
         // WebView versions < 110.0.5438.0 do not contain OOM crashes to a single isolate and
         // instead crash the whole sandbox process. This change is not tracked in a feature flag.
@@ -680,6 +691,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testIsolateCreationAfterCrash() throws Throwable {
         // WebView versions < 110.0.5438.0 do not contain OOM crashes to a single isolate and
         // instead crash the whole sandbox process. This change is not tracked in a feature flag.
@@ -759,6 +771,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testAsyncPromiseCallbacks() throws Throwable {
         // Unlike testPromiseReturn and testPromiseEvaluationThrow, this test is guaranteed to
         // exercise promises in an asynchronous way, rather than in ways which cause a promise to
@@ -827,6 +840,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testLargeScriptJsEvaluation() throws Throwable {
         String longString = "a".repeat(2000000);
         final String code = ""
@@ -851,6 +865,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testLargeScriptByteArrayJsEvaluation() throws Throwable {
         final String longString = "a".repeat(2000000);
         final String codeString = ""
@@ -876,6 +891,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testLargeReturn() throws Throwable {
         final String longString = "a".repeat(2000000);
         final String code = "'a'.repeat(2000000);";
@@ -898,6 +914,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testLargeError() throws Throwable {
         final String longString = "a".repeat(2000000);
         final String code = "throw \"" + longString + "\");";
@@ -924,6 +941,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testResultSizeEnforced() throws Throwable {
         final int maxSize = 100;
         Context context = ApplicationProvider.getApplicationContext();
@@ -974,6 +992,7 @@
 
     @Test
     @LargeTest
+    @Ignore("b/268212217")
     public void testConsoleLogging() throws Throwable {
         final class LoggingJavaScriptConsoleCallback implements JavaScriptConsoleCallback {
             private final Object mLock = new Object();
@@ -1126,6 +1145,7 @@
 
     @Test
     @MediumTest
+    @Ignore("b/268212217")
     public void testConsoleCallbackCanCallService() throws Throwable {
         // This checks that there is nothing intrinsically wrong with calling service APIs from a
         // console client. Note that, in theory, Binder will reuse the same threads if code recurses
diff --git a/javascriptengine/javascriptengine/src/main/AndroidManifest.xml b/javascriptengine/javascriptengine/src/main/AndroidManifest.xml
deleted file mode 100644
index 8e90956..0000000
--- a/javascriptengine/javascriptengine/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java
index df70280..75655f4 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java
@@ -46,7 +46,11 @@
      * The applied limit may not be exact. For example, the limit may internally be rounded up to
      * some multiple of bytes, be increased to some minimum value, or reduced to some maximum
      * supported value.
-     *
+     * <p>
+     * Exceeding this limit will usually result in a {@link MemoryLimitExceededException},
+     * but beware that not all JavaScript sandbox service implementations (particularly older ones)
+     * handle memory exhaustion equally gracefully, and may crash the entire sandbox (see
+     * {@link SandboxDeadException}).
      * @param size heap size in bytes
      */
     @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_ISOLATE_MAX_HEAP_SIZE,
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/MemoryLimitExceededException.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/MemoryLimitExceededException.java
index f3690ee..a0eee81 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/MemoryLimitExceededException.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/MemoryLimitExceededException.java
@@ -20,11 +20,11 @@
 
 /**
  * Indicates that a JavaScriptIsolate's evaluation failed due to exceeding its heap size limit.
- *
- * This exception is thrown when exceeding the heap size limit configured for the isolate via
- * {@link IsolateStartupParameters}, or the default limit. It is not guaranteed to be thrown if the
- * Android system as a whole has run out of memory before the JavaScript environment has reached its
- * configured heap limit.
+ * <p>
+ * This exception may be thrown when exceeding the heap size limit configured for the isolate via
+ * {@link IsolateStartupParameters}, or the default limit. Beware that it will not be thrown if the
+ * Android system as a whole has run out of memory before the JavaScript environment has reached
+ * its configured heap limit.
  * <p>
  * The isolate may not continue to be used after this exception has been thrown, and other pending
  * evalutions for the isolate will fail. The isolate may continue to hold onto resources (even if
@@ -32,6 +32,10 @@
  * sandbox be restarted at the earliest opportunity in order to reclaim these resources.
  * <p>
  * Other isolates within the same sandbox may continue to be used, created, and closed as normal.
+ * <p>
+ * Beware that not all JavaScript sandbox service implementations (particularly older ones)
+ * handle memory exhaustion equally gracefully, and may instead crash the entire sandbox (see
+ * {@link SandboxDeadException}).
  */
 public final class MemoryLimitExceededException extends JavaScriptException {
     public MemoryLimitExceededException(@NonNull String error) {
diff --git a/leanback/leanback-grid/src/main/AndroidManifest.xml b/leanback/leanback-grid/src/main/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/leanback/leanback-grid/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/leanback/leanback-paging/src/main/AndroidManifest.xml b/leanback/leanback-paging/src/main/AndroidManifest.xml
deleted file mode 100644
index 95dabe9..0000000
--- a/leanback/leanback-paging/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/leanback/leanback-preference/src/main/AndroidManifest.xml b/leanback/leanback-preference/src/main/AndroidManifest.xml
deleted file mode 100644
index 107b553..0000000
--- a/leanback/leanback-preference/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<manifest />
diff --git a/leanback/leanback-tab/src/main/AndroidManifest.xml b/leanback/leanback-tab/src/main/AndroidManifest.xml
deleted file mode 100644
index a297096..0000000
--- a/leanback/leanback-tab/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
\ No newline at end of file
diff --git a/leanback/leanback/api/current.ignore b/leanback/leanback/api/current.ignore
index 27e9b7a..bd85f68 100644
--- a/leanback/leanback/api/current.ignore
+++ b/leanback/leanback/api/current.ignore
@@ -21,9 +21,3 @@
     Removed deprecated class androidx.leanback.widget.OnChildSelectedListener
 RemovedInterface: androidx.leanback.widget.ViewHolderTask:
     Removed class androidx.leanback.widget.ViewHolderTask
-
-
-RemovedMethod: androidx.leanback.widget.FacetProvider#getFacet(Class<?>):
-    Removed method androidx.leanback.widget.FacetProvider.getFacet(Class<?>)
-RemovedMethod: androidx.leanback.widget.FacetProviderAdapter#getFacetProvider(int):
-    Removed method androidx.leanback.widget.FacetProviderAdapter.getFacetProvider(int)
diff --git a/leanback/leanback/api/restricted_current.ignore b/leanback/leanback/api/restricted_current.ignore
index 2ac34c7..3295f54 100644
--- a/leanback/leanback/api/restricted_current.ignore
+++ b/leanback/leanback/api/restricted_current.ignore
@@ -23,9 +23,3 @@
     Removed deprecated class androidx.leanback.widget.OnChildSelectedListener
 RemovedInterface: androidx.leanback.widget.ViewHolderTask:
     Removed class androidx.leanback.widget.ViewHolderTask
-
-
-RemovedMethod: androidx.leanback.widget.FacetProvider#getFacet(Class<?>):
-    Removed method androidx.leanback.widget.FacetProvider.getFacet(Class<?>)
-RemovedMethod: androidx.leanback.widget.FacetProviderAdapter#getFacetProvider(int):
-    Removed method androidx.leanback.widget.FacetProviderAdapter.getFacetProvider(int)
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java
index 02e6d11..50f2fee 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java
@@ -59,6 +59,7 @@
 import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -190,6 +191,7 @@
     }
 
     @Test
+    @Ignore("b/281082608")
     public void testPressRightBeforeMainFragmentCreated() throws Throwable {
         final long dataLoadingDelay = 1000;
         Intent intent = new Intent();
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java
index cd4bca8..897d008 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java
@@ -30,6 +30,7 @@
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
+import org.junit.Ignore;
 
 import android.app.Fragment;
 import androidx.leanback.test.R;
@@ -190,6 +191,7 @@
     }
 
     @Test
+    @Ignore("b/281082608")
     public void testFocusWithSpeechRecognizerEnabled() throws Exception {
 
         // Skip the test for devices which do not have SpeechRecognizer
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java
index 804ffa3..32d6e59 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java
@@ -27,6 +27,7 @@
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
+import org.junit.Ignore;
 
 import androidx.fragment.app.Fragment;
 import androidx.leanback.test.R;
@@ -187,6 +188,7 @@
     }
 
     @Test
+    @Ignore("b/281082608")
     public void testFocusWithSpeechRecognizerEnabled() throws Exception {
 
         // Skip the test for devices which do not have SpeechRecognizer
diff --git a/leanback/leanback/src/main/AndroidManifest.xml b/leanback/leanback/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/leanback/leanback/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/libraryversions.toml b/libraryversions.toml
index d5eb02a..413f3f3 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -21,7 +21,7 @@
 COLLECTION = "1.3.0-alpha05"
 COMPOSE = "1.5.0-alpha04"
 COMPOSE_COMPILER = "1.4.7"
-COMPOSE_MATERIAL3 = "1.2.0-alpha01"
+COMPOSE_MATERIAL3 = "1.2.0-alpha02"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-alpha03"
 CONSTRAINTLAYOUT = "2.2.0-alpha10"
@@ -64,7 +64,7 @@
 GRAPHICS_CORE = "1.0.0-alpha04"
 GRAPHICS_FILTERS = "1.0.0-alpha01"
 GRAPHICS_SHAPES = "1.0.0-alpha03"
-GRIDLAYOUT = "1.1.0-alpha02"
+GRIDLAYOUT = "1.1.0-beta01"
 HEALTH_CONNECT = "1.0.0-alpha11"
 HEALTH_SERVICES_CLIENT = "1.0.0-beta04"
 HEIFWRITER = "1.1.0-alpha02"
diff --git a/lifecycle/lifecycle-livedata-core-ktx/src/main/AndroidManifest.xml b/lifecycle/lifecycle-livedata-core-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/lifecycle/lifecycle-livedata-core-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/lifecycle/lifecycle-livedata-core-truth/src/main/AndroidManifest.xml b/lifecycle/lifecycle-livedata-core-truth/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/lifecycle/lifecycle-livedata-core-truth/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/AndroidManifest.xml b/lifecycle/lifecycle-livedata-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/lifecycle/lifecycle-livedata-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/src/main/AndroidManifest.xml b/lifecycle/lifecycle-reactivestreams-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 29b496b..0000000
--- a/lifecycle/lifecycle-reactivestreams-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/lifecycle/lifecycle-runtime-compose/api/current.txt b/lifecycle/lifecycle-runtime-compose/api/current.txt
index 5b19f2b..41aee87c 100644
--- a/lifecycle/lifecycle-runtime-compose/api/current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/current.txt
@@ -10,11 +10,31 @@
 
   public final class LifecycleEffectKt {
     method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+    method @androidx.compose.runtime.Composable public static void LifecycleResumeEffect(optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function1<? super androidx.lifecycle.compose.LifecycleResumePauseEffectScope,? extends androidx.lifecycle.compose.LifecyclePauseEffectResult> effects);
+    method @androidx.compose.runtime.Composable public static void LifecycleStartEffect(optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function1<? super androidx.lifecycle.compose.LifecycleStartStopEffectScope,? extends androidx.lifecycle.compose.LifecycleStopEffectResult> effects);
   }
 
   public final class LifecycleExtKt {
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
   }
 
+  public interface LifecyclePauseEffectResult {
+    method public void runPauseEffect();
+  }
+
+  public final class LifecycleResumePauseEffectScope {
+    ctor public LifecycleResumePauseEffectScope();
+    method public inline androidx.lifecycle.compose.LifecyclePauseEffectResult onPause(kotlin.jvm.functions.Function0<kotlin.Unit> onPauseEffect);
+  }
+
+  public final class LifecycleStartStopEffectScope {
+    ctor public LifecycleStartStopEffectScope();
+    method public inline androidx.lifecycle.compose.LifecycleStopEffectResult onStop(kotlin.jvm.functions.Function0<kotlin.Unit> onStopEffect);
+  }
+
+  public interface LifecycleStopEffectResult {
+    method public void runStopEffect();
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
index 5b19f2b..41aee87c 100644
--- a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
@@ -10,11 +10,31 @@
 
   public final class LifecycleEffectKt {
     method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+    method @androidx.compose.runtime.Composable public static void LifecycleResumeEffect(optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function1<? super androidx.lifecycle.compose.LifecycleResumePauseEffectScope,? extends androidx.lifecycle.compose.LifecyclePauseEffectResult> effects);
+    method @androidx.compose.runtime.Composable public static void LifecycleStartEffect(optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function1<? super androidx.lifecycle.compose.LifecycleStartStopEffectScope,? extends androidx.lifecycle.compose.LifecycleStopEffectResult> effects);
   }
 
   public final class LifecycleExtKt {
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
   }
 
+  public interface LifecyclePauseEffectResult {
+    method public void runPauseEffect();
+  }
+
+  public final class LifecycleResumePauseEffectScope {
+    ctor public LifecycleResumePauseEffectScope();
+    method public inline androidx.lifecycle.compose.LifecyclePauseEffectResult onPause(kotlin.jvm.functions.Function0<kotlin.Unit> onPauseEffect);
+  }
+
+  public final class LifecycleStartStopEffectScope {
+    ctor public LifecycleStartStopEffectScope();
+    method public inline androidx.lifecycle.compose.LifecycleStopEffectResult onStop(kotlin.jvm.functions.Function0<kotlin.Unit> onStopEffect);
+  }
+
+  public interface LifecycleStopEffectResult {
+    method public void runStopEffect();
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
index 5b19f2b..41aee87c 100644
--- a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
@@ -10,11 +10,31 @@
 
   public final class LifecycleEffectKt {
     method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+    method @androidx.compose.runtime.Composable public static void LifecycleResumeEffect(optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function1<? super androidx.lifecycle.compose.LifecycleResumePauseEffectScope,? extends androidx.lifecycle.compose.LifecyclePauseEffectResult> effects);
+    method @androidx.compose.runtime.Composable public static void LifecycleStartEffect(optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function1<? super androidx.lifecycle.compose.LifecycleStartStopEffectScope,? extends androidx.lifecycle.compose.LifecycleStopEffectResult> effects);
   }
 
   public final class LifecycleExtKt {
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
   }
 
+  public interface LifecyclePauseEffectResult {
+    method public void runPauseEffect();
+  }
+
+  public final class LifecycleResumePauseEffectScope {
+    ctor public LifecycleResumePauseEffectScope();
+    method public inline androidx.lifecycle.compose.LifecyclePauseEffectResult onPause(kotlin.jvm.functions.Function0<kotlin.Unit> onPauseEffect);
+  }
+
+  public final class LifecycleStartStopEffectScope {
+    ctor public LifecycleStartStopEffectScope();
+    method public inline androidx.lifecycle.compose.LifecycleStopEffectResult onStop(kotlin.jvm.functions.Function0<kotlin.Unit> onStopEffect);
+  }
+
+  public interface LifecycleStopEffectResult {
+    method public void runStopEffect();
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
index 3d7e9fe..482c851 100644
--- a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
@@ -110,4 +110,78 @@
                 .isEqualTo(1)
         }
     }
+
+    @Test
+    fun lifecycleStartEffectTest() {
+        lifecycleOwner = TestLifecycleOwner(
+            Lifecycle.State.INITIALIZED,
+            dispatcher
+        )
+        var startCount = 0
+        var stopCount = 0
+
+        composeTestRule.waitForIdle()
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                LifecycleStartEffect {
+                    startCount++
+
+                    onStop {
+                        stopCount++
+                    }
+                }
+            }
+        }
+
+        composeTestRule.runOnIdle {
+            assertWithMessage("Lifecycle should not be started (or stopped)")
+                .that(startCount)
+                .isEqualTo(0)
+
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+            assertWithMessage("Lifecycle should have been started")
+                .that(startCount)
+                .isEqualTo(1)
+
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
+            assertWithMessage("Lifecycle should have been stopped")
+                .that(stopCount)
+                .isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun lifecycleResumeEffectTest() {
+        var resumeCount = 0
+        var pauseCount = 0
+
+        composeTestRule.waitForIdle()
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                LifecycleResumeEffect {
+                    resumeCount++
+
+                    onPause {
+                        pauseCount++
+                    }
+                }
+            }
+        }
+
+        composeTestRule.runOnIdle {
+            assertWithMessage("Lifecycle should not be resumed (or paused)")
+                .that(resumeCount)
+                .isEqualTo(0)
+
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            assertWithMessage("Lifecycle should have been resumed")
+                .that(resumeCount)
+                .isEqualTo(1)
+
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+            assertWithMessage("Lifecycle should have been paused")
+                .that(pauseCount)
+                .isEqualTo(1)
+        }
+    }
 }
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
index c9e47ae..245b1a5 100644
--- a/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
@@ -75,4 +75,176 @@
             lifecycleOwner.lifecycle.removeObserver(observer)
         }
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Schedule a pair of effects to run when the [Lifecycle] receives either a
+ * [Lifecycle.Event.ON_START] or [Lifecycle.Event.ON_STOP]. The ON_START effect will
+ * be the body of the [effects] block and the ON_STOP effect will be within the
+ * (onStop clause)[LifecycleStartStopEffectScope.onStop]:
+ *
+ * LifecycleStartEffect(lifecycleOwner) {
+ *     // add ON_START effect work here
+ *
+ *     onStop {
+ *         // add ON_STOP effect work here
+ *     }
+ * }
+ *
+ * A [LifecycleStartEffect] **must** include an [onStop][LifecycleStartStopEffectScope.onStop]
+ * clause as the final statement in its [effects] block. If your operation does not require
+ * an effect for both ON_START and ON_STOP, a [LifecycleEventEffect] should be used instead.
+ *
+ * This function uses a [LifecycleEventObserver] to listen for when [LifecycleStartEffect]
+ * enters the composition and the effects will be launched when receiving a
+ * [Lifecycle.Event.ON_START] or [Lifecycle.Event.ON_STOP] event, respectively.
+ *
+ * This function should **not** be used to launch tasks in response to callback
+ * events by way of storing callback data as a [Lifecycle.State] in a [MutableState].
+ * Instead, see [currentStateAsState] to obtain a [State<Lifecycle.State>][State]
+ * that may be used to launch jobs in response to state changes.
+ *
+ * @param lifecycleOwner The lifecycle owner to attach an observer
+ * @param effects The effects to be launched when we receive the respective event callbacks
+ */
+@Composable
+fun LifecycleStartEffect(
+    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
+    effects: LifecycleStartStopEffectScope.() -> LifecycleStopEffectResult
+) {
+    val lifecycleStartStopEffectScope = LifecycleStartStopEffectScope()
+    // Safely update the current `onStart` lambda when a new one is provided
+    val currentEffects by rememberUpdatedState(effects)
+    DisposableEffect(lifecycleOwner) {
+        val observer = LifecycleEventObserver { _, event ->
+            when (event) {
+                Lifecycle.Event.ON_START ->
+                    lifecycleStartStopEffectScope.currentEffects()
+
+                Lifecycle.Event.ON_STOP ->
+                    lifecycleStartStopEffectScope.currentEffects().runStopEffect()
+
+                else -> {}
+            }
+        }
+
+        lifecycleOwner.lifecycle.addObserver(observer)
+
+        onDispose {
+            lifecycleOwner.lifecycle.removeObserver(observer)
+        }
+    }
+}
+
+/**
+ * Interface used for [LifecycleStartEffect] to run the effect within the onStop
+ * clause when an (ON_STOP)[Lifecycle.Event.ON_STOP] event is received.
+ */
+interface LifecycleStopEffectResult {
+    fun runStopEffect()
+}
+
+/**
+ * Receiver scope for [LifecycleStartEffect] that offers the [onStop] clause to
+ * couple the ON_START effect. This should be the last statement in any call to
+ * [LifecycleStartEffect].
+ */
+class LifecycleStartStopEffectScope {
+    /**
+     * Provide the [onStopEffect] to the [LifecycleStartEffect] to run when the observer
+     * receives an (ON_STOP)[Lifecycle.Event.ON_STOP] event.
+     */
+    inline fun onStop(
+        crossinline onStopEffect: () -> Unit
+    ): LifecycleStopEffectResult = object : LifecycleStopEffectResult {
+        override fun runStopEffect() {
+            onStopEffect()
+        }
+    }
+}
+
+/**
+ * Schedule a pair of effects to run when the [Lifecycle] receives either a
+ * [Lifecycle.Event.ON_RESUME] or [Lifecycle.Event.ON_PAUSE]. The ON_RESUME effect
+ * will be the body of the [effects] block and the ON_PAUSE effect will be within the
+ * (onPause clause)[LifecycleResumePauseEffectScope.onPause]:
+ *
+ * LifecycleResumeEffect(lifecycleOwner) {
+ *     // add ON_RESUME effect work here
+ *
+ *     onPause {
+ *         // add ON_PAUSE effect work here
+ *     }
+ * }
+ *
+ * A [LifecycleResumeEffect] **must** include an [onPause][LifecycleResumePauseEffectScope.onPause]
+ * clause as the final statement in its [effects] block. If your operation does not require
+ * an effect for both ON_RESUME and ON_PAUSE, a [LifecycleEventEffect] should be used instead.
+ *
+ * This function uses a [LifecycleEventObserver] to listen for when [LifecycleResumeEffect]
+ * enters the composition and the effects will be launched when receiving a
+ * [Lifecycle.Event.ON_RESUME] or [Lifecycle.Event.ON_PAUSE] event, respectively.
+ *
+ * This function should **not** be used to launch tasks in response to callback
+ * events by way of storing callback data as a [Lifecycle.State] in a [MutableState].
+ * Instead, see [currentStateAsState] to obtain a [State<Lifecycle.State>][State]
+ * that may be used to launch jobs in response to state changes.
+ *
+ * @param lifecycleOwner The lifecycle owner to attach an observer
+ * @param effects The effects to be launched when we receive the respective event callbacks
+ */
+@Composable
+fun LifecycleResumeEffect(
+    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
+    effects: LifecycleResumePauseEffectScope.() -> LifecyclePauseEffectResult
+) {
+    val lifecycleResumePauseEffectScope = LifecycleResumePauseEffectScope()
+    // Safely update the current `onResume` lambda when a new one is provided
+    val currentEffects by rememberUpdatedState(effects)
+    DisposableEffect(lifecycleOwner) {
+        val observer = LifecycleEventObserver { _, event ->
+            when (event) {
+                Lifecycle.Event.ON_RESUME ->
+                    lifecycleResumePauseEffectScope.currentEffects()
+
+                Lifecycle.Event.ON_PAUSE ->
+                    lifecycleResumePauseEffectScope.currentEffects().runPauseEffect()
+
+                else -> {}
+            }
+        }
+
+        lifecycleOwner.lifecycle.addObserver(observer)
+
+        onDispose {
+            lifecycleOwner.lifecycle.removeObserver(observer)
+        }
+    }
+}
+
+/**
+ * Interface used for [LifecycleResumeEffect] to run the effect within the onPause
+ * clause when an (ON_PAUSE)[Lifecycle.Event.ON_PAUSE] event is received.
+ */
+interface LifecyclePauseEffectResult {
+    fun runPauseEffect()
+}
+
+/**
+ * Receiver scope for [LifecycleResumeEffect] that offers the [onPause] clause to
+ * couple the ON_RESUME effect. This should be the last statement in any call to
+ * [LifecycleResumeEffect].
+ */
+class LifecycleResumePauseEffectScope {
+    /**
+     * Provide the [onPauseEffect] to the [LifecycleResumeEffect] to run when the observer
+     * receives an (ON_PAUSE)[Lifecycle.Event.ON_PAUSE] event.
+     */
+    inline fun onPause(
+        crossinline onPauseEffect: () -> Unit
+    ): LifecyclePauseEffectResult = object : LifecyclePauseEffectResult {
+        override fun runPauseEffect() {
+            onPauseEffect()
+        }
+    }
+}
diff --git a/lifecycle/lifecycle-service/src/main/AndroidManifest.xml b/lifecycle/lifecycle-service/src/main/AndroidManifest.xml
deleted file mode 100644
index 1f3367f..0000000
--- a/lifecycle/lifecycle-service/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2016 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/src/main/AndroidManifest.xml b/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index 20af317..0000000
--- a/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/lifecycle/lifecycle-viewmodel-compose/samples/src/main/AndroidManifest.xml b/lifecycle/lifecycle-viewmodel-compose/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index 20af317..0000000
--- a/lifecycle/lifecycle-viewmodel-compose/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/main/AndroidManifest.xml b/lifecycle/lifecycle-viewmodel-compose/src/main/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/lifecycle/lifecycle-viewmodel-compose/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/lifecycle/lifecycle-viewmodel-ktx/src/main/AndroidManifest.xml b/lifecycle/lifecycle-viewmodel-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/lifecycle/lifecycle-viewmodel-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/lint-checks/src/main/java/androidx/build/lint/aidl/AidlDefinitionDetector.kt b/lint-checks/src/main/java/androidx/build/lint/aidl/AidlDefinitionDetector.kt
index a365b00..16e56b7 100644
--- a/lint-checks/src/main/java/androidx/build/lint/aidl/AidlDefinitionDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/aidl/AidlDefinitionDetector.kt
@@ -44,19 +44,32 @@
     override fun getApplicableFiles() = Scope.OTHER_SCOPE
 
     override fun beforeCheckEachProject(context: Context) {
-        LanguageParserDefinitions.INSTANCE.apply {
+        // Neither LanguageParserDefinitions nor CoreFileTypeRegistry are thread-safe, so this is a
+        // best-effort to avoid a race condition during our own access across multiple lint worker
+        // threads.
+        synchronized(intellijCoreLock) {
+            val aidlFileType = AidlFileType.INSTANCE
+
             // When we run from CLI, the IntelliJ parser (which does not support lexing AIDL) will
             // already be set. Only the first parser will be used, so we need to remove that parser
             // before we add our own.
-            allForLanguage(AidlFileType.INSTANCE.language).forEach { parser ->
-                removeExplicitExtension(AidlFileType.INSTANCE.language, parser)
+            val languageParserDefinitions = LanguageParserDefinitions.INSTANCE
+            languageParserDefinitions.apply {
+                allForLanguage(aidlFileType.language).forEach { parser ->
+                    removeExplicitExtension(aidlFileType.language, parser)
+                }
+                addExplicitExtension(aidlFileType.language, AidlParserDefinition())
             }
-            addExplicitExtension(AidlFileType.INSTANCE.language, AidlParserDefinition())
+
+            // Register our parser for the AIDL file type. Files may be registered more than once to
+            // overwrite the associated extension, but only the first call to `registerFileType`
+            // will associate the file with the name returned by `FileType.getName()`.
+            val coreFileTypeRegistry = CoreFileTypeRegistry.getInstance() as CoreFileTypeRegistry
+            coreFileTypeRegistry.registerFileType(
+                aidlFileType,
+                aidlFileType.defaultExtension
+            )
         }
-        (CoreFileTypeRegistry.getInstance() as CoreFileTypeRegistry).registerFileType(
-            AidlFileType.INSTANCE,
-            AidlFileType.INSTANCE.defaultExtension
-        )
     }
 
     override fun run(context: Context) {
@@ -125,4 +138,10 @@
     containingFile.text,
     textRange.startOffset,
     textRange.endOffset
-)
\ No newline at end of file
+)
+
+/**
+ * Lock object used to synchronize access to IntelliJ registries which are not thread-safe,
+ * including [LanguageParserDefinitions] and [CoreFileTypeRegistry].
+ */
+private val intellijCoreLock = Any()
diff --git a/loader/loader-ktx/src/main/AndroidManifest.xml b/loader/loader-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index b47339d..0000000
--- a/loader/loader-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/loader/loader/src/main/AndroidManifest.xml b/loader/loader/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/loader/loader/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/media/media/src/main/AndroidManifest.xml b/media/media/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/media/media/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/media2/media2-common/src/main/AndroidManifest.xml b/media2/media2-common/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/media2/media2-common/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/media2/media2-exoplayer/src/main/AndroidManifest.xml b/media2/media2-exoplayer/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/media2/media2-exoplayer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/media2/media2-player/src/main/AndroidManifest.xml b/media2/media2-player/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/media2/media2-player/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/media2/media2-session/src/main/AndroidManifest.xml b/media2/media2-session/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/media2/media2-session/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/media2/media2-widget/src/main/AndroidManifest.xml b/media2/media2-widget/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/media2/media2-widget/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/mediarouter/mediarouter-testing/src/main/AndroidManifest.xml b/mediarouter/mediarouter-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/mediarouter/mediarouter-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/mediarouter/mediarouter/src/main/AndroidManifest.xml b/mediarouter/mediarouter/src/main/AndroidManifest.xml
deleted file mode 100644
index bd7af07..0000000
--- a/mediarouter/mediarouter/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java
index 2f7f395..51e6cb0 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java
@@ -28,6 +28,7 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -35,11 +36,16 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatDialog;
@@ -50,6 +56,8 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -69,7 +77,12 @@
 
     // Do not update the route list immediately to avoid unnatural dialog change.
     private static final long UPDATE_ROUTES_DELAY_MS = 300L;
-    static final int MSG_UPDATE_ROUTES = 1;
+    private static final int MSG_UPDATE_ROUTES = 1;
+    private static final int MSG_SHOW_WIFI_HINT = 2;
+    private static final int MSG_SHOW_NO_ROUTES = 3;
+
+    private static final int SHOW_WIFI_HINT_DELAY_MS = 5000;
+    private static final int SHOW_NO_ROUTES_DELAY_MS = 15000;
 
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
@@ -79,6 +92,11 @@
     private ArrayList<MediaRouter.RouteInfo> mRoutes;
     private RouteAdapter mAdapter;
     private ListView mListView;
+    private RelativeLayout mEmptyView;
+    private LinearLayout mSearchingRoutesView;
+    private FrameLayout mNoRoutesView;
+    private FrameLayout mWifiWarningView;
+    private FrameLayout mFooterView;
     private boolean mAttachedToWindow;
     private long mLastUpdateTime;
     @SuppressWarnings({"unchecked", "deprecation"})
@@ -87,12 +105,28 @@
         public void handleMessage(Message message) {
             switch (message.what) {
                 case MSG_UPDATE_ROUTES:
-                    updateRoutes((List<MediaRouter.RouteInfo>) message.obj);
+                    handleUpdateRoutes((List<MediaRouter.RouteInfo>) message.obj);
+                    break;
+                case MSG_SHOW_WIFI_HINT:
+                    handleShowNoWifiWarning();
+                    break;
+                case MSG_SHOW_NO_ROUTES:
+                    handleShowNoRoutes();
                     break;
             }
         }
     };
 
+    private static final int FINDING_DEVICES = 0;
+    private static final int SHOWING_ROUTES = 1;
+    private static final int NO_DEVICES_NO_WIFI_HINT = 2;
+    private static final int NO_ROUTES = 3;
+
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FINDING_DEVICES, SHOWING_ROUTES, NO_DEVICES_NO_WIFI_HINT, NO_ROUTES})
+    private @interface MediaRouterChooserDialogState {}
+
     public MediaRouteChooserDialog(@NonNull Context context) {
         this(context, 0);
     }
@@ -196,6 +230,20 @@
         mListView.setEmptyView(findViewById(android.R.id.empty));
         mTitleView = findViewById(R.id.mr_chooser_title);
 
+        mEmptyView = findViewById(R.id.mr_empty_view);
+        mSearchingRoutesView = findViewById(R.id.mr_chooser_searching);
+        mNoRoutesView = findViewById(R.id.mr_chooser_no_routes);
+        mWifiWarningView = findViewById(R.id.mr_chooser_wifi_warning);
+        mFooterView = findViewById(R.id.mr_chooser_footer);
+
+        TextView zeroRoutesDescription = findViewById(R.id.mr_chooser_zero_routes_description);
+        TextView wifiWarningDescription = findViewById(R.id.mr_chooser_wifi_warning_description);
+        Button doneButton = findViewById(R.id.mr_chooser_done_button);
+
+        zeroRoutesDescription.setMovementMethod(LinkMovementMethod.getInstance());
+        wifiWarningDescription.setMovementMethod(LinkMovementMethod.getInstance());
+        doneButton.setOnClickListener(view -> dismiss());
+
         updateLayout();
     }
 
@@ -210,17 +258,26 @@
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
-
         mAttachedToWindow = true;
         mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
         refreshRoutes();
+
+        mHandler.removeMessages(MSG_SHOW_WIFI_HINT);
+        mHandler.removeMessages(MSG_SHOW_NO_ROUTES);
+        mHandler.removeMessages(MSG_UPDATE_ROUTES);
+
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MSG_SHOW_WIFI_HINT), SHOW_WIFI_HINT_DELAY_MS);
     }
 
     @Override
     public void onDetachedFromWindow() {
         mAttachedToWindow = false;
+
         mRouter.removeCallback(mCallback);
         mHandler.removeMessages(MSG_UPDATE_ROUTES);
+        mHandler.removeMessages(MSG_SHOW_WIFI_HINT);
+        mHandler.removeMessages(MSG_SHOW_NO_ROUTES);
 
         super.onDetachedFromWindow();
     }
@@ -234,7 +291,7 @@
             onFilterRoutes(routes);
             Collections.sort(routes, RouteComparator.sInstance);
             if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) {
-                updateRoutes(routes);
+                handleUpdateRoutes(routes);
             } else {
                 mHandler.removeMessages(MSG_UPDATE_ROUTES);
                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ROUTES, routes),
@@ -243,11 +300,105 @@
         }
     }
 
-    void updateRoutes(List<MediaRouter.RouteInfo> routes) {
+    void handleUpdateRoutes(List<MediaRouter.RouteInfo> routes) {
         mLastUpdateTime = SystemClock.uptimeMillis();
         mRoutes.clear();
         mRoutes.addAll(routes);
         mAdapter.notifyDataSetChanged();
+
+        mHandler.removeMessages(MSG_SHOW_NO_ROUTES);
+        mHandler.removeMessages(MSG_SHOW_WIFI_HINT);
+
+        if (routes.isEmpty()) {
+            // When all routes are removed or disconnected
+            updateViewForState(FINDING_DEVICES);
+
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(MSG_SHOW_WIFI_HINT), SHOW_WIFI_HINT_DELAY_MS);
+        } else {
+            updateViewForState(SHOWING_ROUTES);
+        }
+    }
+
+    void handleShowNoWifiWarning() {
+        if (mRoutes.isEmpty()) {
+            updateViewForState(NO_DEVICES_NO_WIFI_HINT);
+            mHandler.removeMessages(MSG_SHOW_WIFI_HINT);
+            mHandler.removeMessages(MSG_SHOW_NO_ROUTES);
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_NO_ROUTES),
+                    SHOW_NO_ROUTES_DELAY_MS);
+        }
+    }
+
+    void handleShowNoRoutes() {
+        if (mRoutes.isEmpty()) {
+            updateViewForState(NO_ROUTES);
+            mHandler.removeMessages(MSG_SHOW_WIFI_HINT);
+            mHandler.removeMessages(MSG_SHOW_NO_ROUTES);
+            mHandler.removeMessages(MSG_UPDATE_ROUTES);
+            mRouter.removeCallback(mCallback);
+        }
+    }
+
+    void updateViewForState(@MediaRouterChooserDialogState int state) {
+        switch (state) {
+            case NO_ROUTES:
+                updateViewForNoRoutes();
+                break;
+            case NO_DEVICES_NO_WIFI_HINT:
+                updateViewForNoDevicesNoWifiHint();
+                break;
+            case SHOWING_ROUTES:
+                updateViewForShowingRoutes();
+                break;
+            case FINDING_DEVICES:
+                updateViewForFindingDevices();
+                break;
+        }
+    }
+
+    private void updateViewForNoRoutes() {
+        setTitle(R.string.mr_chooser_zero_routes_found_title);
+        mTitleView.setVisibility(View.VISIBLE);
+        mListView.setVisibility(View.GONE);
+        mEmptyView.setVisibility(View.VISIBLE);
+        mFooterView.setVisibility(View.VISIBLE);
+        mNoRoutesView.setVisibility(View.VISIBLE);
+        mSearchingRoutesView.setVisibility(View.GONE);
+        mWifiWarningView.setVisibility(View.GONE);
+    }
+
+    private void updateViewForNoDevicesNoWifiHint() {
+        setTitle(R.string.mr_chooser_title);
+        mTitleView.setVisibility(View.VISIBLE);
+        mListView.setVisibility(View.GONE);
+        mEmptyView.setVisibility(View.VISIBLE);
+        mFooterView.setVisibility(View.GONE);
+        mNoRoutesView.setVisibility(View.GONE);
+        mSearchingRoutesView.setVisibility(View.VISIBLE);
+        mWifiWarningView.setVisibility(View.VISIBLE);
+    }
+
+    private void updateViewForShowingRoutes() {
+        setTitle(R.string.mr_chooser_title);
+        mTitleView.setVisibility(View.VISIBLE);
+        mListView.setVisibility(View.VISIBLE);
+        mEmptyView.setVisibility(View.GONE);
+        mFooterView.setVisibility(View.GONE);
+        mNoRoutesView.setVisibility(View.GONE);
+        mSearchingRoutesView.setVisibility(View.GONE);
+        mWifiWarningView.setVisibility(View.GONE);
+    }
+
+    private void updateViewForFindingDevices() {
+        setTitle(R.string.mr_chooser_title);
+        mTitleView.setVisibility(View.VISIBLE);
+        mListView.setVisibility(View.GONE);
+        mEmptyView.setVisibility(View.VISIBLE);
+        mFooterView.setVisibility(View.GONE);
+        mNoRoutesView.setVisibility(View.GONE);
+        mSearchingRoutesView.setVisibility(View.VISIBLE);
+        mWifiWarningView.setVisibility(View.GONE);
     }
 
     private static final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
@@ -288,8 +439,9 @@
             return getItem(position).isEnabled();
         }
 
+        @NonNull
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
+        public View getView(int position, View convertView, @NonNull ViewGroup parent) {
             View view = convertView;
             if (view == null) {
                 view = mInflater.inflate(R.layout.mr_chooser_list_item, parent, false);
@@ -356,7 +508,7 @@
         private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
             // If the type of the receiver device is specified, use it.
             switch (route.getDeviceType()) {
-                case  MediaRouter.RouteInfo.DEVICE_TYPE_TV:
+                case MediaRouter.RouteInfo.DEVICE_TYPE_TV:
                     return mTvIcon;
                 case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
                     return mSpeakerIcon;
@@ -407,4 +559,4 @@
             return lhs.getName().compareToIgnoreCase(rhs.getName());
         }
     }
-}
+}
\ No newline at end of file
diff --git a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
index 966e42c..c8978f7 100644
--- a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
+++ b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
@@ -15,44 +15,111 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="fill_parent"
-             android:layout_height="wrap_content"
-             android:orientation="vertical">
-    <TextView android:id="@+id/mr_chooser_title"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:minHeight="52dp"
-              android:paddingTop="12dp"
-              android:paddingLeft="24dp"
-              android:paddingRight="24dp"
-              android:gravity="center_vertical"
-              android:text="@string/mr_chooser_title"
-              android:singleLine="true"
-              android:ellipsize="end"
-              android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
-    <ListView android:id="@+id/mr_chooser_list"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:paddingBottom="24dp"
-              android:divider="@android:color/transparent"
-              android:dividerHeight="0dp" />
-    <LinearLayout android:id="@android:id/empty"
-              android:layout_width="fill_parent"
-              android:layout_height="240dp"
-              android:orientation="vertical"
-              android:paddingTop="90dp"
-              android:paddingLeft="16dp"
-              android:paddingRight="16dp"
-              android:visibility="gone">
-        <TextView android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:layout_gravity="center"
-                  android:text="@string/mr_chooser_searching"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
-        <ProgressBar android:layout_width="150dp"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="center"
-                     android:indeterminate="true"
-                     style="?android:attr/progressBarStyleHorizontal" />
-    </LinearLayout>
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:padding="24dp">
+
+    <TextView
+        android:id="@+id/mr_chooser_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:text="@string/mr_chooser_title"
+        android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
+
+    <ListView
+        android:id="@+id/mr_chooser_list"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:divider="@android:color/transparent"
+        android:dividerHeight="0dp"
+        android:visibility="gone" />
+
+    <RelativeLayout
+        android:id="@+id/mr_empty_view"
+        android:layout_width="fill_parent"
+        android:layout_height="240dp"
+        android:visibility="visible">
+
+        <LinearLayout
+            android:id="@+id/mr_chooser_searching"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:orientation="vertical"
+            android:visibility="visible">
+
+            <ProgressBar
+                android:id="@+id/mr_chooser_search_progress_bar"
+                style="?android:attr/progressBarStyleHorizontal"
+                android:layout_width="150dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:indeterminate="true" />
+
+            <TextView
+                android:id="@+id/mr_chooser_search_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:text="@string/mr_chooser_searching"
+                android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
+
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/mr_chooser_no_routes"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone">
+
+            <TextView
+                android:id="@+id/mr_chooser_zero_routes_description"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/mr_chooser_zero_routes_description"
+                android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
+
+        </FrameLayout>
+
+        <FrameLayout
+            android:id="@+id/mr_chooser_wifi_warning"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone">
+
+            <TextView
+                android:id="@+id/mr_chooser_wifi_warning_description"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/mr_chooser_wifi_warning_description"
+                android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
+
+        </FrameLayout>
+
+        <FrameLayout
+            android:id="@+id/mr_chooser_footer"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:gravity="bottom"
+            android:visibility="gone">
+
+            <Button
+                android:id="@+id/mr_chooser_done_button"
+                style="?android:attr/borderlessButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="end|right"
+                android:text="@string/mr_chooser_done_button_label" />
+
+        </FrameLayout>
+
+    </RelativeLayout>
+
 </LinearLayout>
diff --git a/mediarouter/mediarouter/src/main/res/values/strings.xml b/mediarouter/mediarouter/src/main/res/values/strings.xml
index 42d088d..8b54407 100644
--- a/mediarouter/mediarouter/src/main/res/values/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values/strings.xml
@@ -94,4 +94,16 @@
 
     <!-- Placeholder text for cast dialog title view [CHAR LIMIT=50] -->
     <string name="mr_cast_dialog_title_view_placeholder">No info available</string>
+
+    <!-- Title of the media route chooser dialog when zero routes have been found after searching. [CHAR_LIMIT=NONE] -->
+    <string name="mr_chooser_zero_routes_found_title">No devices available</string>
+
+    <!-- Text displayed when no routes have been found after searching. [CHAR_LIMIT=NONE] -->
+    <string name="mr_chooser_zero_routes_description">Unable to find devices, make sure your device and the Cast device are on the same Wi-Fi network and try again.</string>
+
+    <!-- Text displayed to hint making sure device and cast device are connected to the same Wi-Fi. [CHAR_LIMIT=NONE] -->
+    <string name="mr_chooser_wifi_warning_description">Make sure your device and the Cast device are on the same Wi-Fi network.</string>
+
+    <!-- The label of the Done button that closes the Cast media route chooser dialog when tapped. [CHAR_LIMIT=30] -->
+    <string name="mr_chooser_done_button_label">Done</string>
 </resources>
diff --git a/metrics/metrics-benchmark/src/main/AndroidManifest.xml b/metrics/metrics-benchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 29b496b..0000000
--- a/metrics/metrics-benchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/DelayedView.kt b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/DelayedView.kt
index f447748..0129f1f 100644
--- a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/DelayedView.kt
+++ b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/DelayedView.kt
@@ -2,6 +2,8 @@
 
 import android.content.Context
 import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
 import android.os.Build
 import android.util.AttributeSet
 import android.view.View
@@ -15,6 +17,11 @@
     var repetitions: Int = 0
     var maxReps: Int = 0
     var perFrameStateData: List<JankStatsTest.FrameStateInputData> = listOf()
+    val textPaint = Paint()
+
+    init {
+       textPaint.textSize = 50f
+    }
 
     @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
     override fun onDraw(canvas: Canvas) {
@@ -29,8 +36,10 @@
             (((Math.random() * 127) + 128).toInt() shl 16) or
             (((Math.random() * 127) + 128).toInt() shl 8) or
             ((Math.random() * 127) + 128).toInt()
+        textPaint.setColor(Color.BLACK)
 
         canvas.drawColor(randomColor)
+        canvas.drawText("Frame ${repetitions - 1}", 200f, 200f, textPaint)
         if (perFrameStateData.isNotEmpty()) {
             val metricsState = PerformanceMetricsState.getHolderForHierarchy(this).state!!
             val stateData = perFrameStateData[repetitions - 1]
diff --git a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
index 5351167..5bb3c0d 100644
--- a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
+++ b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
@@ -185,15 +185,11 @@
         jankStats.jankHeuristicMultiplier = 0f
         runDelayTest(frameDelay, NUM_FRAMES, latchedListener)
         // FrameMetrics sometimes drops a frame, so the total number of
-        // jankData items might be less than NUM_FRAMES
+        // jankData items might be less than NUM_FRAMES. Check against actual
+        // number of frames received instead.
         assertEquals(
-            "jank frames != NUMFRAMES",
-            NUM_FRAMES, latchedListener.numJankFrames
-        )
-        assertTrue(
-            "With heuristicMultiplier 0, should be at least ${NUM_FRAMES - 1} " +
-                "frames with jank data, not ${latchedListener.numJankFrames}",
-            latchedListener.numJankFrames >= (NUM_FRAMES - 1)
+            "numJankFrames != numFrames",
+            latchedListener.numFrames, latchedListener.numJankFrames
         )
     }
 
@@ -275,12 +271,12 @@
         frameInit.initFramePipeline()
 
         runDelayTest(frameDelay, NUM_FRAMES, latchedListener)
+
         // FrameMetrics sometimes drops a frame, so the total number of
         // jankData items might be less than NUM_FRAMES
-        assertTrue(
-            "There should be at least ${NUM_FRAMES - 1} frames with jank data, " +
-                "not ${latchedListener.jankData.size}",
-            latchedListener.jankData.size >= (NUM_FRAMES - 1)
+        assertEquals("There should be ${latchedListener.numFrames} frames " +
+                "with jank data, not ${latchedListener.jankData.size}",
+            latchedListener.numFrames, latchedListener.jankData.size
         )
         latchedListener.reset()
 
@@ -307,8 +303,8 @@
         metricsState.putSingleFrameState(state2.key, state2.value)
         runDelayTest(frameDelay, NUM_FRAMES, latchedListener)
         assertEquals(
-            "frameDelay 100: There should be $NUM_FRAMES frames with jank data", NUM_FRAMES,
-            latchedListener.jankData.size
+            "frameDelay 100: There should be ${latchedListener.numFrames} frames with" +
+                "jank data", latchedListener.numFrames, latchedListener.jankData.size
         )
         var item0: FrameData = latchedListener.jankData[0]
         assertEquals("There should be 3 states at frame 0", 3,
@@ -323,7 +319,7 @@
         assertThat(state2, Matchers.isIn(item0.states))
 
         // Now test the rest of the frames, which should not include singleFrameState state2
-        for (i in 1 until NUM_FRAMES) {
+        for (i in 1 until latchedListener.numFrames) {
             val item = latchedListener.jankData[i]
             assertEquals("There should be 2 states at frame $i", 2,
                 item.states.size)
@@ -503,18 +499,46 @@
         runDelayTest(frameDelay = 0, numFrames = perFrameStateData.size,
             latchedListener, perFrameStateData)
 
-        assertEquals("There should be ${expectedResults.size} frames of data",
-            expectedResults.size, latchedListener.jankData.size)
-        for (i in 0 until expectedResults.size) {
-            val testResultStates = latchedListener.jankData[i].states
-            val expectedResult = expectedResults[i]
-            assertEquals("There should be ${expectedResult.size} states",
-                expectedResult.size, testResultStates.size)
-            for (state in testResultStates) {
-                assertEquals("State value not correct",
-                    state.value, expectedResult.get(state.key))
+        // There might be one or two dropped frames, check that we have nearly the number
+        // expected
+        assertTrue("There should be at least ${expectedResults.size - 2} frames of data",
+            (latchedListener.jankData.size > expectedResults.size - 2))
+
+        /*
+        Ideally, we would check each frame's result states against the expected results.
+        But the system sometimes drops frames, causing the jankData to be a subset of
+        the expectedResults set from above. This is fine, for testing purposes, but that
+        means we should check the current result against the expected result of this and
+        the next frame, to account for these skips. when this happens, we increment the
+        expected index since all results will be offset by that skip.
+         */
+        var expectedIndex = 0
+        var resultIndex = 0
+        while (expectedIndex < expectedResults.size) {
+            val testResultStates = latchedListener.jankData[resultIndex].states
+            // Test against this and next expected result, in case system skipped a frame
+            var matched = checkFrameStates(expectedResults[expectedIndex], testResultStates)
+            if (!matched) {
+                expectedIndex++
+                matched = checkFrameStates(expectedResults[expectedIndex], testResultStates)
+            }
+            assertTrue("States do not match at frame $expectedIndex", matched)
+            expectedIndex++
+            resultIndex++
+        }
+    }
+
+    private fun checkFrameStates(
+        expectedResult: Map<String, String>,
+        testResultStates: List<StateInfo>
+    ): Boolean {
+        if (expectedResult.size != testResultStates.size) return false
+        for (state in testResultStates) {
+            if (state.value != expectedResult.get(state.key)) {
+               return false
             }
         }
+        return true
     }
 
     private fun runDelayTest(
diff --git a/metrics/metrics-performance/src/main/AndroidManifest.xml b/metrics/metrics-performance/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a68baf..0000000
--- a/metrics/metrics-performance/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/navigation/CHANGELOG.md b/navigation/CHANGELOG.md
index 5c13fd8..e80842d 100644
--- a/navigation/CHANGELOG.md
+++ b/navigation/CHANGELOG.md
@@ -14,9 +14,15 @@
 # Behavior Change
 # External Contributions
 
-# Unreleased
+## Unreleased
+
+### Bug Fixes
+
+- Updated the error message and exception type when navigating on a NavController with no
+  navigation graph set.
 
 ### Dependency Updates
 
-* Changed dependency of Activity library from version 1.6.1 to version 1.7.1.
+- Changed dependency of Activity library from version 1.6.1 to version 1.7.1.
+
 
diff --git a/navigation/navigation-common-ktx/src/main/AndroidManifest.xml b/navigation/navigation-common-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index fdfdce5..0000000
--- a/navigation/navigation-common-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/AndroidManifest.xml b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/navigation/navigation-compose/samples/src/main/AndroidManifest.xml b/navigation/navigation-compose/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/navigation/navigation-compose/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/navigation/navigation-compose/src/androidTest/AndroidManifest.xml b/navigation/navigation-compose/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/navigation/navigation-compose/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/navigation/navigation-compose/src/main/AndroidManifest.xml b/navigation/navigation-compose/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/navigation/navigation-compose/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml b/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 8358852..0000000
--- a/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/navigation/navigation-fragment-ktx/src/main/AndroidManifest.xml b/navigation/navigation-fragment-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 0e03c43..0000000
--- a/navigation/navigation-fragment-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/navigation/navigation-runtime-ktx/src/androidTest/AndroidManifest.xml b/navigation/navigation-runtime-ktx/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 8358852..0000000
--- a/navigation/navigation-runtime-ktx/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/navigation/navigation-runtime-ktx/src/main/AndroidManifest.xml b/navigation/navigation-runtime-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index fdfdce5..0000000
--- a/navigation/navigation-runtime-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/navigation/navigation-runtime-truth/src/main/AndroidManifest.xml b/navigation/navigation-runtime-truth/src/main/AndroidManifest.xml
deleted file mode 100644
index fdfdce5..0000000
--- a/navigation/navigation-runtime-truth/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index be6eafb..fc7b988e 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -1605,7 +1605,7 @@
      * @param navOptions special options for this navigation operation
      * @param navigatorExtras extras to pass to the Navigator
      *
-     * @throws IllegalStateException if there is no current navigation node
+     * @throws IllegalStateException if navigation graph has not been set for this NavController
      * @throws IllegalArgumentException if the desired destination cannot be found from the
      *                                  current destination
      */
@@ -1622,7 +1622,10 @@
                 _graph
             else
                 backQueue.last().destination
-            ) ?: throw IllegalStateException("no current navigation node")
+            ) ?: throw IllegalStateException(
+                "No current destination found. Ensure a navigation graph has been set for " +
+                    "NavController $this."
+            )
 
         @IdRes
         var destId = resId
diff --git a/navigation/navigation-ui-ktx/src/main/AndroidManifest.xml b/navigation/navigation-ui-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index fdfdce5..0000000
--- a/navigation/navigation-ui-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/paging/integration-tests/testapp/src/androidTest/kotlin/androidx/paging/integration/testapp/v3/OnPagesUpdatedTest.kt b/paging/integration-tests/testapp/src/androidTest/kotlin/androidx/paging/integration/testapp/v3/OnPagesUpdatedTest.kt
index a615eb5..fe5a24b 100644
--- a/paging/integration-tests/testapp/src/androidTest/kotlin/androidx/paging/integration/testapp/v3/OnPagesUpdatedTest.kt
+++ b/paging/integration-tests/testapp/src/androidTest/kotlin/androidx/paging/integration/testapp/v3/OnPagesUpdatedTest.kt
@@ -36,6 +36,7 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeout
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -50,6 +51,7 @@
 
     @OptIn(ExperimentalCoroutinesApi::class)
     @Test
+    @Ignore("b/278918559")
     fun onPagesUpdatedFlow() = runBlocking {
         val scenario = scenarioRule.scenario
 
diff --git a/paging/paging-compose/integration-tests/paging-demos/src/main/AndroidManifest.xml b/paging/paging-compose/integration-tests/paging-demos/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/paging/paging-compose/integration-tests/paging-demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/paging/paging-compose/integration-tests/paging-demos/src/main/java/androidx/paging/compose/demos/room/PagingRoomSample.kt b/paging/paging-compose/integration-tests/paging-demos/src/main/java/androidx/paging/compose/demos/room/PagingRoomSample.kt
index e16c7b7..cdcd575 100644
--- a/paging/paging-compose/integration-tests/paging-demos/src/main/java/androidx/paging/compose/demos/room/PagingRoomSample.kt
+++ b/paging/paging-compose/integration-tests/paging-demos/src/main/java/androidx/paging/compose/demos/room/PagingRoomSample.kt
@@ -23,7 +23,7 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -118,7 +118,7 @@
                 key = lazyPagingItems.itemKey { user -> user.id },
             ) { index ->
                 val user = lazyPagingItems[index]
-                var counter by rememberSaveable { mutableStateOf(0) }
+                var counter by rememberSaveable { mutableIntStateOf(0) }
                 Text(
                     text = "counter=$counter index=$index ${user?.name} ${user?.id}",
                     fontSize = 50.sp,
diff --git a/paging/paging-compose/samples/src/main/AndroidManifest.xml b/paging/paging-compose/samples/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/paging/paging-compose/samples/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/paging/paging-compose/src/androidTest/AndroidManifest.xml b/paging/paging-compose/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/paging/paging-compose/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/paging/paging-compose/src/main/AndroidManifest.xml b/paging/paging-compose/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/paging/paging-compose/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/paging/paging-runtime-ktx/src/main/AndroidManifest.xml b/paging/paging-runtime-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/paging/paging-runtime-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/paging/paging-runtime/src/main/AndroidManifest.xml b/paging/paging-runtime/src/main/AndroidManifest.xml
deleted file mode 100644
index 7b5bc6e..0000000
--- a/paging/paging-runtime/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/paging/paging-runtime/src/main/java/androidx/paging/NullPaddedListDiffHelper.kt b/paging/paging-runtime/src/main/java/androidx/paging/NullPaddedListDiffHelper.kt
index af09d964..08550a5 100644
--- a/paging/paging-runtime/src/main/java/androidx/paging/NullPaddedListDiffHelper.kt
+++ b/paging/paging-runtime/src/main/java/androidx/paging/NullPaddedListDiffHelper.kt
@@ -90,27 +90,6 @@
     )
 }
 
-private class OffsettingListUpdateCallback internal constructor(
-    private val offset: Int,
-    private val callback: ListUpdateCallback
-) : ListUpdateCallback {
-    override fun onInserted(position: Int, count: Int) {
-        callback.onInserted(position + offset, count)
-    }
-
-    override fun onRemoved(position: Int, count: Int) {
-        callback.onRemoved(position + offset, count)
-    }
-
-    override fun onMoved(fromPosition: Int, toPosition: Int) {
-        callback.onMoved(fromPosition + offset, toPosition + offset)
-    }
-
-    override fun onChanged(position: Int, count: Int, payload: Any?) {
-        callback.onChanged(position + offset, count, payload)
-    }
-}
-
 /**
  * See NullPaddedDiffing.md for how this works and why it works that way :).
  *
diff --git a/paging/paging-rxjava2-ktx/src/main/AndroidManifest.xml b/paging/paging-rxjava2-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/paging/paging-rxjava2-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/paging/paging-rxjava2/src/androidTest/AndroidManifest.xml b/paging/paging-rxjava2/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/paging/paging-rxjava2/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/paging/paging-rxjava3/src/androidTest/AndroidManifest.xml b/paging/paging-rxjava3/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/paging/paging-rxjava3/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/paging/paging-testing/api/current.txt b/paging/paging-testing/api/current.txt
index f10ecc1..91eeba55 100644
--- a/paging/paging-testing/api/current.txt
+++ b/paging/paging-testing/api/current.txt
@@ -27,6 +27,7 @@
 
   public final class StaticListPagingSourceFactoryKt {
     method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
+    method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(java.util.List<? extends Value>);
   }
 
   public final class TestPager<Key, Value> {
diff --git a/paging/paging-testing/api/public_plus_experimental_current.txt b/paging/paging-testing/api/public_plus_experimental_current.txt
index f10ecc1..91eeba55 100644
--- a/paging/paging-testing/api/public_plus_experimental_current.txt
+++ b/paging/paging-testing/api/public_plus_experimental_current.txt
@@ -27,6 +27,7 @@
 
   public final class StaticListPagingSourceFactoryKt {
     method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
+    method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(java.util.List<? extends Value>);
   }
 
   public final class TestPager<Key, Value> {
diff --git a/paging/paging-testing/api/restricted_current.txt b/paging/paging-testing/api/restricted_current.txt
index f10ecc1..91eeba55 100644
--- a/paging/paging-testing/api/restricted_current.txt
+++ b/paging/paging-testing/api/restricted_current.txt
@@ -27,6 +27,7 @@
 
   public final class StaticListPagingSourceFactoryKt {
     method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
+    method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(java.util.List<? extends Value>);
   }
 
   public final class TestPager<Key, Value> {
diff --git a/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt b/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt
index 3cf9165..2a719a6 100644
--- a/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt
+++ b/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt
@@ -17,6 +17,7 @@
 package androidx.paging.testing
 
 import androidx.paging.InvalidatingPagingSourceFactory
+import androidx.paging.LoadType.REFRESH
 import androidx.paging.PagingSource
 import androidx.paging.Pager
 import androidx.paging.PagingSourceFactory
@@ -25,16 +26,15 @@
 import kotlinx.coroutines.launch
 
 /**
- * Returns a factory that creates [PagingSource] instances.
+ * Returns a [PagingSourceFactory] that creates [PagingSource] instances.
  *
- * Since this method returns a lambda, call [this.invoke] to create a new PagingSource. Can be
- * used as the pagingSourceFactory when constructing a [Pager]. The same factory should be reused
- * within the lifetime of a ViewModel.
+ * Can be used as the pagingSourceFactory when constructing a [Pager] in tests. The same factory
+ * should be reused within the lifetime of a ViewModel.
  *
  * Extension method on a [Flow] of list that represents the data source, with each static list
  * representing a generation of data from which a [PagingSource] will load from. With every
- * emission, the current [PagingSource] will be invalidated, thereby triggering a new generation
- * of Paged data.
+ * emission to the flow, the current [PagingSource] will be invalidated, thereby triggering
+ * a new generation of Paged data.
  *
  * Supports multiple factories and thus multiple collection on the same flow.
  *
@@ -61,4 +61,18 @@
     }
 
     return factory
-}
\ No newline at end of file
+}
+
+/**
+ * Returns a [PagingSourceFactory] that creates [PagingSource] instances.
+ *
+ * Can be used as the pagingSourceFactory when constructing a [Pager] in tests. The same factory
+ * should be reused within the lifetime of a ViewModel.
+ *
+ * Extension method on a [List] of data from which a [PagingSource] will load from. While this
+ * factory supports multi-generational operations such as [REFRESH], it does not support updating
+ * the data source. This means any PagingSources generated by the same factory will load from
+ * the exact same list of data.
+ */
+public fun <Value : Any> List<Value>.asPagingSourceFactory(): PagingSourceFactory<Int, Value> =
+    PagingSourceFactory { StaticListPagingSource(this) }
diff --git a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt
index 312d95b..ca7351a 100644
--- a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt
+++ b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt
@@ -63,6 +63,11 @@
         loadDelay
     )
 
+    private fun createSingleGenFactory(data: List<Int>) = WrappedPagingSourceFactory(
+        data.asPagingSourceFactory(),
+        loadDelay
+    )
+
     @Before
     fun init() {
         Dispatchers.setMain(UnconfinedTestDispatcher())
@@ -73,7 +78,20 @@
         val dataFlow = flowOf(List(30) { it })
         val pager = createPager(dataFlow)
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this) {}
+            val snapshot = pager.asSnapshot(this)
+            // first page + prefetched page
+            assertThat(snapshot).containsExactlyElementsIn(
+                listOf(0, 1, 2, 3, 4, 5, 6, 7)
+            )
+        }
+    }
+
+    @Test
+    fun initialRefreshSingleGen() {
+        val data = List(30) { it }
+        val pager = createPager(data)
+        testScope.runTest {
+            val snapshot = pager.asSnapshot(this)
             // first page + prefetched page
             assertThat(snapshot).containsExactlyElementsIn(
                 listOf(0, 1, 2, 3, 4, 5, 6, 7)
@@ -86,7 +104,7 @@
         val dataFlow = flowOf(List(30) { it })
         val pager = createPager(dataFlow)
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this)
+            val snapshot = pager.asSnapshot(this) {}
             // first page + prefetched page
             assertThat(snapshot).containsExactlyElementsIn(
                 listOf(0, 1, 2, 3, 4, 5, 6, 7)
@@ -103,7 +121,7 @@
             }
         }
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this) {}
+            val snapshot = pager.asSnapshot(this)
             // loads 8[initial 5 + prefetch 3] items total, including separators
             assertThat(snapshot).containsExactlyElementsIn(
                 listOf(0, "sep", 1, "sep", 2, "sep", 3, "sep", 4)
@@ -116,7 +134,7 @@
         val dataFlow = flowOf(List(30) { it })
         val pager = createPagerNoPrefetch(dataFlow)
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this) {}
+            val snapshot = pager.asSnapshot(this)
 
             assertThat(snapshot).containsExactlyElementsIn(
                 listOf(0, 1, 2, 3, 4)
@@ -129,7 +147,7 @@
         val dataFlow = flowOf(List(30) { it })
         val pager = createPager(dataFlow, 10)
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this) {}
+            val snapshot = pager.asSnapshot(this)
 
             assertThat(snapshot).containsExactlyElementsIn(
                 listOf(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)
@@ -142,7 +160,7 @@
         val dataFlow = flowOf(List(30) { it })
         val pager = createPagerNoPrefetch(dataFlow, 10)
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this) {}
+            val snapshot = pager.asSnapshot(this)
 
             assertThat(snapshot).containsExactlyElementsIn(
                 listOf(10, 11, 12, 13, 14)
@@ -155,7 +173,20 @@
         val dataFlow = emptyFlow<List<Int>>()
         val pager = createPager(dataFlow)
         testScope.runTest {
-            val snapshot = pager.asSnapshot(this) {}
+            val snapshot = pager.asSnapshot(this)
+
+            assertThat(snapshot).containsExactlyElementsIn(
+                emptyList<Int>()
+            )
+        }
+    }
+
+    @Test
+    fun emptyInitialRefreshSingleGen() {
+        val data = emptyList<Int>()
+        val pager = createPager(data)
+        testScope.runTest {
+            val snapshot = pager.asSnapshot(this)
 
             assertThat(snapshot).containsExactlyElementsIn(
                 emptyList<Int>()
@@ -191,6 +222,37 @@
     }
 
     @Test
+    fun manualRefreshSingleGen() {
+        val data = List(30) { it }
+        val pager = createPager(data)
+        testScope.runTest {
+            val snapshot = pager.asSnapshot(this) {
+                refresh()
+            }
+            assertThat(snapshot).containsExactlyElementsIn(
+                listOf(0, 1, 2, 3, 4, 5, 6, 7),
+            )
+        }
+    }
+
+    @Test
+    fun manualRefreshSingleGen_pagingSourceInvalidated() {
+        val data = List(30) { it }
+        val sources = mutableListOf<PagingSource<Int, Int>>()
+        val factory = data.asPagingSourceFactory()
+        val pager = Pager(
+            config = PagingConfig(pageSize = 3, initialLoadSize = 5),
+            pagingSourceFactory = { factory().also { sources.add(it) } },
+        ).flow
+        testScope.runTest {
+            pager.asSnapshot(this) {
+                refresh()
+            }
+            assertThat(sources.first().invalid).isTrue()
+        }
+    }
+
+    @Test
     fun manualEmptyRefresh() {
         val dataFlow = emptyFlow<List<Int>>()
         val pager = createPagerNoPrefetch(dataFlow)
@@ -2312,6 +2374,13 @@
             initialKey
         )
 
+    private fun createPager(data: List<Int>, initialKey: Int = 0) =
+        Pager(
+            PagingConfig(pageSize = 3, initialLoadSize = 5),
+            initialKey,
+            createSingleGenFactory(data),
+        ).flow
+
     private fun createPagerNoPlaceholders(dataFlow: Flow<List<Int>>, initialKey: Int = 0) =
         createPager(
             dataFlow,
diff --git a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt
index eb28936..c293ed0 100644
--- a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt
+++ b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt
@@ -196,4 +196,78 @@
             listOf(30, 31, 32, 33, 34)
         )
     }
+
+    @Test
+    fun singleListFactory_refresh() = testScope.runTest {
+        val data = List(20) { it }
+        val factory = data.asPagingSourceFactory()
+
+        val pagingSource1 = factory()
+        val pager1 = TestPager(pagingSource1, CONFIG)
+        val refresh1 = pager1.refresh() as Page
+        assertThat(refresh1.data).containsExactlyElementsIn(
+            listOf(0, 1, 2, 3, 4)
+        )
+
+        val pagingSource2 = factory()
+        val pager2 = TestPager(pagingSource2, CONFIG)
+        val refresh2 = pager2.refresh() as Page
+        assertThat(refresh2.data).containsExactlyElementsIn(
+            listOf(0, 1, 2, 3, 4)
+        )
+    }
+
+    @Test
+    fun singleListFactory_empty() = testScope.runTest {
+        val data = emptyList<Int>()
+        val factory = data.asPagingSourceFactory()
+
+        val pagingSource1 = factory()
+        val pager1 = TestPager(pagingSource1, CONFIG)
+        val refresh1 = pager1.refresh() as Page
+        assertThat(refresh1.data).isEmpty()
+
+        val pagingSource2 = factory()
+        val pager2 = TestPager(pagingSource2, CONFIG)
+        val refresh2 = pager2.refresh() as Page
+        assertThat(refresh2.data).isEmpty()
+    }
+
+    @Test
+    fun singleListFactory_append() = testScope.runTest {
+        val data = List(20) { it }
+        val factory = data.asPagingSourceFactory()
+        val pagingSource1 = factory()
+        val pager1 = TestPager(pagingSource1, CONFIG)
+
+        pager1.refresh() as Page
+        pager1.append()
+        assertThat(pager1.getPages().flatMap { it.data }).containsExactlyElementsIn(
+            listOf(0, 1, 2, 3, 4, 5, 6, 7)
+        )
+
+        pager1.append()
+        assertThat(pager1.getPages().flatMap { it.data }).containsExactlyElementsIn(
+            listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+        )
+    }
+
+    @Test
+    fun singleListFactory_prepend() = testScope.runTest {
+        val data = List(20) { it }
+        val factory = data.asPagingSourceFactory()
+        val pagingSource1 = factory()
+        val pager1 = TestPager(pagingSource1, CONFIG)
+
+        pager1.refresh(initialKey = 10) as Page
+        pager1.prepend()
+        assertThat(pager1.getPages().flatMap { it.data }).containsExactlyElementsIn(
+            listOf(7, 8, 9, 10, 11, 12, 13, 14)
+        )
+
+        pager1.prepend()
+        assertThat(pager1.getPages().flatMap { it.data }).containsExactlyElementsIn(
+            listOf(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
+        )
+    }
 }
\ No newline at end of file
diff --git a/palette/palette-ktx/src/main/AndroidManifest.xml b/palette/palette-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/palette/palette-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/palette/palette/src/main/AndroidManifest.xml b/palette/palette/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/palette/palette/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/percentlayout/percentlayout/src/main/AndroidManifest.xml b/percentlayout/percentlayout/src/main/AndroidManifest.xml
deleted file mode 100644
index 5197378..0000000
--- a/percentlayout/percentlayout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index fec6fdf..68b1459 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
 # Disable docs
 androidx.enableDocumentation=false
 androidx.playground.snapshotBuildId=10041883
-androidx.playground.metalavaBuildId=10009114
+androidx.playground.metalavaBuildId=10074764
 androidx.studio.type=playground
diff --git a/preference/preference-ktx/src/main/AndroidManifest.xml b/preference/preference-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/preference/preference-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/preference/preference/src/main/AndroidManifest.xml b/preference/preference/src/main/AndroidManifest.xml
deleted file mode 100644
index 5197378..0000000
--- a/preference/preference/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/print/print/src/main/AndroidManifest.xml b/print/print/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/print/print/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
index 049e853..5104e2a 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
@@ -36,6 +36,7 @@
 import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_NOT_FOUND
 import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.toLoadCompatSdkException
 import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import java.lang.ref.WeakReference
 import java.util.WeakHashMap
 import java.util.concurrent.Executor
 import kotlinx.coroutines.suspendCancellableCoroutine
@@ -346,7 +347,7 @@
 
     companion object {
 
-        private val sInstances = WeakHashMap<Context, SdkSandboxManagerCompat>()
+        private val sInstances = WeakHashMap<Context, WeakReference<SdkSandboxManagerCompat>>()
 
         /**
          *  Creates [SdkSandboxManagerCompat].
@@ -358,7 +359,8 @@
         @JvmStatic
         fun from(context: Context): SdkSandboxManagerCompat {
             synchronized(sInstances) {
-                var instance = sInstances[context]
+                val reference = sInstances[context]
+                var instance = reference?.get()
                 if (instance == null) {
                     val configHolder = LocalSdkConfigsHolder.load(context)
                     val localSdks = LocallyLoadedSdks()
@@ -371,7 +373,7 @@
                         localSdks,
                         sdkLoader
                     )
-                    sInstances[context] = instance
+                    sInstances[context] = WeakReference(instance)
                 }
                 return instance
             }
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
index 84d3466..68778460 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
@@ -43,6 +43,7 @@
 import org.junit.Assert.assertTrue
 import org.junit.Assume
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -277,6 +278,7 @@
     }
 
     @Test
+    @Ignore("b/272324246")
     fun onConfigurationChangedTest() {
         val layout = activity.findViewById<LinearLayout>(R.id.mainlayout)
 
diff --git a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
index 5084dc1..a2ea2da 100644
--- a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
+++ b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
@@ -182,6 +182,7 @@
     }
 
     @Test
+    @Ignore("b/272324246")
     fun testConfigurationChanged() {
         val configChangedLatch = CountDownLatch(1)
         val adapter = TestSandboxedUiAdapter(
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/AndroidManifest.xml b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 5b41847..0000000
--- a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml b/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index ed173d5..0000000
--- a/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/profileinstaller/integration-tests/profile-verification/src/androidTest/AndroidManifest.xml b/profileinstaller/integration-tests/profile-verification/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index bae036b..0000000
--- a/profileinstaller/integration-tests/profile-verification/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/profileinstaller/integration-tests/profile-verification/src/main/AndroidManifest.xml b/profileinstaller/integration-tests/profile-verification/src/main/AndroidManifest.xml
deleted file mode 100644
index d4c1970..0000000
--- a/profileinstaller/integration-tests/profile-verification/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/profileinstaller/profileinstaller-benchmark/src/main/AndroidManifest.xml b/profileinstaller/profileinstaller-benchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 29b496b..0000000
--- a/profileinstaller/profileinstaller-benchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/recommendation/recommendation/src/main/AndroidManifest.xml b/recommendation/recommendation/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/recommendation/recommendation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/recyclerview/recyclerview-selection/src/main/AndroidManifest.xml b/recyclerview/recyclerview-selection/src/main/AndroidManifest.xml
deleted file mode 100644
index 8162281..0000000
--- a/recyclerview/recyclerview-selection/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerCacheTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerCacheTest.java
index dd7781b..e5076f6 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerCacheTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerCacheTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Build;
+import androidx.test.filters.FlakyTest;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SdkSuppress;
@@ -92,6 +93,7 @@
 
     @MediumTest
     @Test
+    @FlakyTest(bugId = 281085288)
     public void cacheAndPrefetch() throws Throwable {
         final Config config = (Config) mConfig.clone();
         setupByConfig(config);
diff --git a/recyclerview/recyclerview/src/main/AndroidManifest.xml b/recyclerview/recyclerview/src/main/AndroidManifest.xml
deleted file mode 100644
index 3c8a63b..0000000
--- a/recyclerview/recyclerview/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
\ No newline at end of file
diff --git a/room/room-common/src/main/java/androidx/room/Delete.kt b/room/room-common/src/main/java/androidx/room/Delete.kt
index 176fd26..fbae415 100644
--- a/room/room-common/src/main/java/androidx/room/Delete.kt
+++ b/room/room-common/src/main/java/androidx/room/Delete.kt
@@ -34,7 +34,7 @@
  *     public fun deleteSongs(vararg songs: Song)
  *
  *     @Delete
- *     public fun deleteAlbumAndSongs(val album: Album, val songs: List<Song>)
+ *     public fun deleteAlbumAndSongs(album: Album, songs: List<Song>)
  * }
  * ```
  *
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
index 213eeb9..1b31225 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
@@ -89,16 +89,7 @@
             .newInstance(this)
     }
     fun hasAnnotationViaReflection(qName: String): Boolean {
-        val ksType = if (
-            // Note: Technically, we could just make KSTypeWrapper internal and cast to get the
-            // delegate, but since we need to use reflection anyway, just get it via reflection.
-            this.javaClass.canonicalName == "androidx.room.compiler.processing.ksp.KSTypeWrapper") {
-            this.javaClass.methods.find { it.name == "getDelegate" }?.invoke(this)
-        } else {
-            this
-        }
-        val kotlinType =
-            ksType?.javaClass?.methods?.find { it.name == "getKotlinType" }?.invoke(ksType)
+        val kotlinType = javaClass.methods.find { it.name == "getKotlinType" }?.invoke(this)
         val kotlinAnnotations =
             kotlinType?.javaClass
                 ?.methods
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt
index 6150713..a40fe41 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt
@@ -35,7 +35,6 @@
 import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.JTypeVariableName
 import com.squareup.kotlinpoet.javapoet.JWildcardTypeName
-import kotlin.coroutines.Continuation
 
 // Catch-all type name when we cannot resolve to anything. This is what KAPT uses as error type
 // and we use the same type in KSP for consistency.
@@ -150,16 +149,8 @@
     return when (variance) {
         Variance.CONTRAVARIANT -> JWildcardTypeName.supertypeOf(resolveTypeName())
         Variance.COVARIANT -> JWildcardTypeName.subtypeOf(resolveTypeName())
-        Variance.STAR -> {
-            JWildcardTypeName.subtypeOf(JTypeName.OBJECT)
-        }
-        else -> {
-            if (hasJvmWildcardAnnotation()) {
-                JWildcardTypeName.subtypeOf(resolveTypeName())
-            } else {
-                resolveTypeName()
-            }
-        }
+        Variance.STAR -> JWildcardTypeName.subtypeOf(JTypeName.OBJECT)
+        else -> resolveTypeName()
     }
 }
 
@@ -179,28 +170,13 @@
 ): JTypeName {
     return if (this.arguments.isNotEmpty() && !resolver.isJavaRawType(this)) {
         val args: Array<JTypeName> = this.arguments
-            .map { typeArg ->
-                typeArg.asJTypeName(
-                    resolver = resolver,
-                    typeArgumentTypeLookup = typeArgumentTypeLookup
-                )
-            }
+            .map { typeArg -> typeArg.asJTypeName(resolver, typeArgumentTypeLookup) }
             .map { it.tryBox() }
-            .let { args ->
-                if (this.isSuspendFunctionType) args.convertToSuspendSignature()
-                else args
-            }
             .toTypedArray()
 
-        when (
-            val typeName = declaration
-                .asJTypeName(resolver, typeArgumentTypeLookup).tryBox()
-        ) {
+        when (val typeName = declaration.asJTypeName(resolver, typeArgumentTypeLookup).tryBox()) {
             is JArrayTypeName -> JArrayTypeName.of(args.single())
-            is JClassName -> JParameterizedTypeName.get(
-                typeName,
-                *args
-            )
+            is JClassName -> JParameterizedTypeName.get(typeName, *args)
             else -> error("Unexpected type name for KSType: $typeName")
         }
     } else {
@@ -209,29 +185,6 @@
 }
 
 /**
- * Transforms [this] list of arguments to a suspend signature. For a [suspend] functional type, we
- * need to transform it to be a FunctionX with a [Continuation] with the correct return type. A
- * transformed SuspendFunction looks like this:
- *
- * FunctionX<[? super $params], ? super Continuation<? super $ReturnType>, ?>
- */
-private fun List<JTypeName>.convertToSuspendSignature(): List<JTypeName> {
-    val args = this
-
-    // The last arg is the return type, so take everything except the last arg
-    val actualArgs = args.subList(0, args.size - 1)
-    val continuationReturnType = JWildcardTypeName.supertypeOf(args.last())
-    val continuationType = JParameterizedTypeName.get(
-        JClassName.get(Continuation::class.java),
-        continuationReturnType
-    )
-    return actualArgs + listOf(
-        JWildcardTypeName.supertypeOf(continuationType),
-        JWildcardTypeName.subtypeOf(JTypeName.OBJECT)
-    )
-}
-
-/**
  * The private constructor of [JTypeVariableName] which receives a list.
  * We use this in [createModifiableTypeVariableName] to create a [JTypeVariableName] whose bounds
  * can be modified afterwards.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
index d797d50..56c3455 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing.ksp
 
+import androidx.room.compiler.processing.rawTypeName
 import com.google.devtools.ksp.KspExperimental
 import com.google.devtools.ksp.isOpen
 import com.google.devtools.ksp.processing.Resolver
@@ -25,9 +26,9 @@
 import com.google.devtools.ksp.symbol.KSTypeAlias
 import com.google.devtools.ksp.symbol.KSTypeArgument
 import com.google.devtools.ksp.symbol.KSTypeParameter
-import com.google.devtools.ksp.symbol.KSTypeReference
 import com.google.devtools.ksp.symbol.Modifier
 import com.google.devtools.ksp.symbol.Variance
+import com.squareup.kotlinpoet.javapoet.JClassName
 
 /**
  * When kotlin generates java code, it has some interesting rules on how variance is handled.
@@ -51,131 +52,62 @@
      * @param scope The [KSTypeVarianceResolverScope] associated with the given type.
      */
     @OptIn(KspExperimental::class)
-    fun applyTypeVariance(type: KSType, scope: KSTypeVarianceResolverScope): KSType {
+    fun applyTypeVariance(type: KSType, scope: KSTypeVarianceResolverScope?): KSType {
         if (type.isError ||
             type.arguments.isEmpty() ||
             resolver.isJavaRawType(type) ||
-            !scope.needsWildcardResolution) {
+            scope?.needsWildcardResolution == false) {
             // There's nothing to resolve in this case, so just return the original type.
             return type
         }
 
-        // First, replace any type aliases in the type with their actual types
-        return type.replaceTypeAliases()
+        // First wrap types/arguments in our own wrappers so that we can keep track of the original
+        // type, which is needed to get annotations.
+        return KSTypeWrapper(resolver, type)
             // Next, resolve wildcards based on the scope of the type
             .resolveWildcards(scope)
             // Next, apply any additional variance changes based on the @JvmSuppressWildcards or
             // @JvmWildcard annotations on the resolved type.
-            .applyJvmWildcardAnnotations()
+            .applyJvmWildcardAnnotations(scope)
             // Finally, unwrap any delegate types. (Note: as part of resolving wildcards, we wrap
             // types/type arguments in delegates to avoid loosing annotation information. However,
             // those delegates may cause issues later if KSP tries to cast the type/argument to a
             // particular implementation, so we unwrap them here.
-            .removeWrappers()
+            .unwrap()
     }
 
-    private fun KSType.replaceTypeAliases(typeStack: ReferenceStack = ReferenceStack()): KSType {
-        if (isError || typeStack.queue.contains(this)) {
-            return this
-        }
-        if (declaration is KSTypeAlias) {
-            return (declaration as KSTypeAlias).type.resolve().replaceTypeAliases(typeStack)
-        }
-        return typeStack.withReference(this) {
-            createWrapper(arguments.map { it.replaceTypeAliases(typeStack) })
-        }
+    private fun KSTypeWrapper.resolveWildcards(
+        scope: KSTypeVarianceResolverScope?
+    ) = if (scope == null) {
+        this
+    } else if (hasTypeVariables(scope.declarationType())) {
+        // If the associated declared type contains type variables that were resolved, e.g.
+        // using "asMemberOf", then it has special rules about how to resolve the types.
+        getJavaWildcardWithTypeVariables(
+            declarationType = KSTypeWrapper(resolver, scope.declarationType())
+                .getJavaWildcard(scope),
+            scope = scope,
+        )
+    } else {
+        getJavaWildcard(scope)
     }
 
-    private fun KSTypeArgument.replaceTypeAliases(typeStack: ReferenceStack): KSTypeArgument {
-        val type = type?.resolve()
-        if (
-            type == null ||
-            type.isError ||
-            variance == Variance.STAR ||
-            typeStack.queue.contains(type)
-        ) {
-            return this
-        }
-        return createWrapper(type.replaceTypeAliases(typeStack), variance)
-    }
-
-    private fun KSType.resolveWildcards(scope: KSTypeVarianceResolverScope): KSType {
-        return if (hasTypeVariables(scope.declarationType())) {
-            // If the associated declared type contains type variables that were resolved, e.g.
-            // using "asMemberOf", then it has special rules about how to resolve the types.
-            getJavaWildcardWithTypeVariables(
-                type = this,
-                declarationType = getJavaWildcard(scope.declarationType(), scope),
-                scope = scope,
-            )
-        } else {
-            getJavaWildcard(this, scope)
-        }
-    }
-
-    private fun hasTypeVariables(
-        type: KSType?,
-        stack: ReferenceStack = ReferenceStack()
-    ): Boolean {
-        if (type == null || type.isError || stack.queue.contains(type)) {
+    private fun hasTypeVariables(type: KSType?, stack: List<KSType> = emptyList()): Boolean {
+        if (type == null || type.isError || stack.contains(type)) {
             return false
         }
-        return stack.withReference(type) {
-            type.isTypeParameter() ||
-                type.arguments.any { hasTypeVariables(it.type?.resolve(), stack) }
-        }
+        return type.isTypeParameter() ||
+            type.arguments.any { hasTypeVariables(it.type?.resolve(), stack + type) }
     }
 
-    private fun getJavaWildcard(
-        type: KSType,
-        scope: KSTypeVarianceResolverScope,
-        typeStack: ReferenceStack = ReferenceStack(),
-        typeArgStack: List<KSTypeArgument> = emptyList(),
-        typeParamStack: List<KSTypeParameter> = emptyList(),
-    ): KSType {
-        if (type.isError || typeStack.queue.contains(type)) {
-            return type
-        }
-        return typeStack.withReference(type) {
-            val resolvedTypeArgs =
-                type.arguments.indices.map { i ->
-                    getJavaWildcard(
-                        typeArg = type.arguments[i],
-                        typeParam = type.declaration.typeParameters[i],
-                        scope = scope,
-                        typeStack = typeStack,
-                        typeArgStack = typeArgStack,
-                        typeParamStack = typeParamStack,
-                    )
-                }
-            type.createWrapper(resolvedTypeArgs)
-        }
-    }
+    private fun KSTypeWrapper.getJavaWildcard(scope: KSTypeVarianceResolverScope) =
+        replace(arguments.map { it.getJavaWildcard(scope) })
 
-    private fun getJavaWildcard(
-        typeArg: KSTypeArgument,
-        typeParam: KSTypeParameter,
-        scope: KSTypeVarianceResolverScope,
-        typeStack: ReferenceStack,
-        typeArgStack: List<KSTypeArgument>,
-        typeParamStack: List<KSTypeParameter>,
-    ): KSTypeArgument {
-        val type = typeArg.type?.resolve()
-        if (
-            type == null ||
-            type.isError ||
-            typeArg.variance == Variance.STAR ||
-            typeStack.queue.contains(type)
-        ) {
-            return typeArg
-        }
-        val resolvedType = getJavaWildcard(
-            type = type,
-            scope = scope,
-            typeStack = typeStack,
-            typeArgStack = typeArgStack + typeArg,
-            typeParamStack = typeParamStack + typeParam
-        )
+    private fun KSTypeArgumentWrapper.getJavaWildcard(
+        scope: KSTypeVarianceResolverScope
+    ): KSTypeArgumentWrapper {
+        val type = type ?: return this
+        val resolvedType = type.getJavaWildcard(scope)
         fun inheritDeclarationSiteVariance(): Boolean {
             // Before we check the current variance, we need to check the previous variance in the
             // stack to see if they allow us to inherit the current variance, and that logic differs
@@ -186,8 +118,8 @@
                 if (typeParamStack.indices.none { i ->
                         (typeParamStack[i].variance == Variance.CONTRAVARIANT ||
                             typeArgStack[i].variance == Variance.CONTRAVARIANT) &&
-                            // The declaration and use site variance is ignored when using @JvmWildcard
-                            // explicitly on a type.
+                            // The declaration and use site variance is ignored when using
+                            // @JvmWildcard explicitly on a type.
                             !typeArgStack[i].hasJvmWildcardAnnotation()
                     }) {
                     return false
@@ -237,74 +169,48 @@
         }
         val resolvedVariance = if (inheritDeclarationSiteVariance()) {
             typeParam.variance
-        } else if (typeParam.variance == typeArg.variance) {
+        } else if (typeParam.variance == variance) {
             // If we're not applying the declaration-site variance, and the use-site variance is the
             // same as the declaration-site variance then we don't include the use-site variance in
             // the jvm type either.
             Variance.INVARIANT
         } else {
-            typeArg.variance
+            variance
         }
-        return typeArg.createWrapper(resolvedType, resolvedVariance)
+        return replace(resolvedType, resolvedVariance)
     }
 
-    private fun getJavaWildcardWithTypeVariables(
-        type: KSType,
-        declarationType: KSType? = null,
+    private fun KSTypeWrapper.getJavaWildcardWithTypeVariables(
         scope: KSTypeVarianceResolverScope,
-        typeStack: ReferenceStack = ReferenceStack(),
+        declarationType: KSTypeWrapper?,
+    ) = if (declarationType?.isTypeParameter() == false) {
+        replace(
+            declarationType.arguments.indices.map { i ->
+                arguments[i].getJavaWildcardWithTypeVariablesForOuterType(
+                    declarationTypeArg = declarationType.arguments[i],
+                    scope = scope,
+                )
+            }
+        )
+    } else {
+        getJavaWildcardWithTypeVariablesForInnerType(scope)
+    }
+
+    private fun KSTypeWrapper.getJavaWildcardWithTypeVariablesForInnerType(
+        scope: KSTypeVarianceResolverScope,
         typeParamStack: List<KSTypeParameter> = emptyList(),
-    ): KSType {
-        if (type.isError || typeStack.queue.contains(type)) {
-            return type
-        }
-        return typeStack.withReference(type) {
-            val resolvedTypeArgs =
-                if (declarationType != null && !declarationType.isTypeParameter()) {
-                    declarationType.arguments.indices.map { i ->
-                        getJavaWildcardWithTypeVariablesForOuterType(
-                            typeArg = type.arguments[i],
-                            declarationTypeArg = declarationType.arguments[i],
-                            scope = scope,
-                            typeStack = typeStack,
-                        )
-                    }
-                } else {
-                    type.arguments.indices.map { i ->
-                        getJavaWildcardWithTypeVariablesForInnerType(
-                            typeArg = type.arguments[i],
-                            typeParam = type.declaration.typeParameters[i],
-                            scope = scope,
-                            typeStack = typeStack,
-                            typeParamStack = typeParamStack
-                        )
-                    }
-                }
-            type.createWrapper(resolvedTypeArgs)
-        }
-    }
+    ) = replace(
+        arguments.map { it.getJavaWildcardWithTypeVariablesForInnerType(scope, typeParamStack) }
+    )
 
-    private fun getJavaWildcardWithTypeVariablesForInnerType(
-        typeArg: KSTypeArgument,
-        typeParam: KSTypeParameter,
+    private fun KSTypeArgumentWrapper.getJavaWildcardWithTypeVariablesForInnerType(
         scope: KSTypeVarianceResolverScope,
-        typeStack: ReferenceStack,
         typeParamStack: List<KSTypeParameter>,
-    ): KSTypeArgument {
-        val type = typeArg.type?.resolve()
-        if (
-            type == null ||
-            type.isError ||
-            typeArg.variance == Variance.STAR ||
-            typeStack.queue.contains(type)
-        ) {
-            return typeArg
-        }
-        val resolvedType = getJavaWildcardWithTypeVariables(
-            type = type,
+    ): KSTypeArgumentWrapper {
+        val type = type ?: return this
+        val resolvedType = type.getJavaWildcardWithTypeVariablesForInnerType(
             scope = scope,
-            typeStack = typeStack,
-            typeParamStack = typeParamStack + typeParam,
+            typeParamStack = typeParamStack + typeParam
         )
         val resolvedVariance = if (
             typeParam.variance != Variance.INVARIANT &&
@@ -319,137 +225,52 @@
         ) {
             typeParam.variance
         } else {
-            typeArg.variance
+            variance
         }
-        return typeArg.createWrapper(resolvedType, resolvedVariance)
+        return replace(resolvedType, resolvedVariance)
     }
 
-    private fun getJavaWildcardWithTypeVariablesForOuterType(
-        typeArg: KSTypeArgument,
-        declarationTypeArg: KSTypeArgument,
+    private fun KSTypeArgumentWrapper.getJavaWildcardWithTypeVariablesForOuterType(
+        declarationTypeArg: KSTypeArgumentWrapper,
         scope: KSTypeVarianceResolverScope,
-        typeStack: ReferenceStack,
-    ): KSTypeArgument {
-        val type = typeArg.type?.resolve()
-        if (
-            type == null ||
-            type.isError ||
-            typeArg.variance == Variance.STAR ||
-            typeStack.queue.contains(type)
-        ) {
-            return typeArg
-        }
-        val resolvedType = getJavaWildcardWithTypeVariables(
-            type = type,
-            declarationType = declarationTypeArg.type?.resolve(),
+    ): KSTypeArgumentWrapper {
+        val type = type ?: return this
+        val resolvedType = type.getJavaWildcardWithTypeVariables(
+            declarationType = declarationTypeArg.type,
             scope = scope,
-            typeStack = typeStack
         )
         val resolvedVariance = if (declarationTypeArg.variance != Variance.INVARIANT) {
             declarationTypeArg.variance
         } else {
-            typeArg.variance
-        }
-        return typeArg.createWrapper(resolvedType, resolvedVariance)
-    }
-
-    private fun KSType.applyJvmWildcardAnnotations(
-        typeStack: ReferenceStack = ReferenceStack(),
-        typeArgStack: List<KSTypeArgument> = emptyList(),
-    ): KSType {
-        if (isError || typeStack.queue.contains(this)) {
-            return this
-        }
-        return typeStack.withReference(this) {
-            val resolvedTypeArgs =
-                arguments.indices.map { i ->
-                    applyJvmWildcardAnnotations(
-                        typeArg = arguments[i],
-                        typeParameter = declaration.typeParameters[i],
-                        typeArgStack = typeArgStack,
-                        typeStack = typeStack,
-                    )
-                }
-            createWrapper(resolvedTypeArgs)
-        }
-    }
-
-    private fun applyJvmWildcardAnnotations(
-        typeArg: KSTypeArgument,
-        typeParameter: KSTypeParameter,
-        typeStack: ReferenceStack,
-        typeArgStack: List<KSTypeArgument>,
-    ): KSTypeArgument {
-        val type = typeArg.type?.resolve()
-        if (
-            type == null ||
-            type.isError ||
-            typeArg.variance == Variance.STAR ||
-            typeStack.queue.contains(type)
-        ) {
-            return typeArg
-        }
-        val resolvedType = type.applyJvmWildcardAnnotations(typeStack, typeArgStack + typeArg)
-        val resolvedVariance = when {
-            typeParameter.variance == Variance.INVARIANT &&
-                typeArg.variance != Variance.INVARIANT -> typeArg.variance
-            typeArg.hasJvmWildcardAnnotation() -> typeParameter.variance
-            // We only need to check the first type in the stack for @JvmSuppressWildcards.
-            // Any other @JvmSuppressWildcards usages will be placed on the type arguments rather
-            // than the types, so no need to check the rest of the types.
-            typeStack.queue.first().hasSuppressJvmWildcardAnnotation() ||
-                typeArg.hasSuppressWildcardsAnnotationInHierarchy() ||
-                typeArgStack.any { it.hasSuppressJvmWildcardAnnotation() } ||
-                typeParameter.hasSuppressWildcardsAnnotationInHierarchy() -> Variance.INVARIANT
-            else -> typeArg.variance
-        }
-        return typeArg.createWrapper(resolvedType, resolvedVariance)
-    }
-
-    private fun KSTypeArgument.createWrapper(
-        newType: KSType,
-        newVariance: Variance
-    ): KSTypeArgument {
-        return KSTypeArgumentWrapper(
-            delegate = (this as? KSTypeArgumentWrapper)?.delegate ?: this,
-            type = newType.createTypeReference(),
-            variance = newVariance
-        )
-    }
-
-    private fun KSType.createWrapper(newArguments: List<KSTypeArgument>): KSType {
-        return KSTypeWrapper(
-            delegate = (this as? KSTypeWrapper)?.delegate ?: this,
-            arguments = newArguments
-        )
-    }
-
-    private fun KSType.removeWrappers(typeStack: ReferenceStack = ReferenceStack()): KSType {
-        if (isError || typeStack.queue.contains(this)) {
-            return this
-        }
-        return typeStack.withReference(this) {
-            val delegateType = (this as? KSTypeWrapper)?.delegate ?: this
-            delegateType.replace(arguments.map { it.removeWrappers(typeStack) })
-        }
-    }
-
-    private fun KSTypeArgument.removeWrappers(
-        typeStack: ReferenceStack = ReferenceStack()
-    ): KSTypeArgument {
-        val type = type?.resolve()
-        if (
-            type == null ||
-            type.isError ||
-            variance == Variance.STAR ||
-            typeStack.queue.contains(type)
-        ) {
-            return this
-        }
-        return resolver.getTypeArgument(
-            type.removeWrappers(typeStack).createTypeReference(),
             variance
-        )
+        }
+        return replace(resolvedType, resolvedVariance)
+    }
+
+    private fun KSTypeWrapper.applyJvmWildcardAnnotations(
+        scope: KSTypeVarianceResolverScope?
+    ) =
+        replace(arguments.map { it.applyJvmWildcardAnnotations(scope) })
+
+    private fun KSTypeArgumentWrapper.applyJvmWildcardAnnotations(
+        scope: KSTypeVarianceResolverScope?
+    ): KSTypeArgumentWrapper {
+        val type = type ?: return this
+        val resolvedType = type.applyJvmWildcardAnnotations(scope)
+        val resolvedVariance = when {
+            typeParam.variance == Variance.INVARIANT && variance != Variance.INVARIANT -> variance
+            hasJvmWildcardAnnotation() -> typeParam.variance
+            scope?.hasSuppressWildcards == true ||
+                // We only need to check the first type in the stack for @JvmSuppressWildcards.
+                // Any other @JvmSuppressWildcards usages will be placed on the type arguments
+                // rather than the types, so no need to check the rest of the types.
+                typeStack.first().hasSuppressJvmWildcardAnnotation() ||
+                this.hasSuppressWildcardsAnnotationInHierarchy() ||
+                typeArgStack.any { it.hasSuppressJvmWildcardAnnotation() } ||
+                typeParam.hasSuppressWildcardsAnnotationInHierarchy() -> Variance.INVARIANT
+            else -> variance
+        }
+        return replace(resolvedType, resolvedVariance)
     }
 }
 
@@ -460,14 +281,80 @@
  * [KSType#replace(KSTypeArgument)] directly when using [KSTypeArgumentWrapper] or we'll get an
  * [IllegalStateException] since KSP tries to cast to its own implementation of [KSTypeArgument].
  */
-private class KSTypeWrapper(
-    val delegate: KSType,
-    override val arguments: List<KSTypeArgument>
-) : KSType by delegate {
-    override fun toString() = if (arguments.isNotEmpty()) {
-        "${delegate.toString().substringBefore("<")}<${arguments.joinToString(",")}>"
-    } else {
-        delegate.toString()
+private class KSTypeWrapper constructor(
+    private val resolver: Resolver,
+    private val originalType: KSType,
+    private val newType: KSType =
+        originalType.replaceTypeAliases().replaceSuspendFunctionTypes(resolver),
+    newTypeArguments: List<KSTypeArgumentWrapper>? = null,
+    private val typeStack: List<KSTypeWrapper> = emptyList(),
+    private val typeArgStack: List<KSTypeArgumentWrapper> = emptyList(),
+    private val typeParamStack: List<KSTypeParameter> = emptyList(),
+) {
+    val declaration = originalType.declaration
+
+    val arguments: List<KSTypeArgumentWrapper> by lazy {
+        newTypeArguments ?: newType.arguments.indices.map { i ->
+            KSTypeArgumentWrapper(
+                originalTypeArg = newType.arguments[i],
+                typeParam = newType.declaration.typeParameters[i],
+                resolver = resolver,
+                typeStack = typeStack + this,
+                typeArgStack = typeArgStack,
+                typeParamStack = typeParamStack,
+            )
+        }
+    }
+
+    fun replace(newTypeArguments: List<KSTypeArgumentWrapper>) = KSTypeWrapper(
+        originalType = originalType,
+        newType = newType,
+        newTypeArguments = newTypeArguments,
+        resolver = resolver,
+        typeStack = typeStack,
+        typeArgStack = typeArgStack,
+        typeParamStack = typeParamStack,
+    )
+
+    fun hasSuppressJvmWildcardAnnotation() = originalType.hasSuppressJvmWildcardAnnotation()
+
+    fun isTypeParameter() = originalType.isTypeParameter()
+
+    fun unwrap() = newType.replace(arguments.map { it.unwrap() })
+
+    override fun toString() = buildString {
+        if (originalType.annotations.toList().isNotEmpty()) {
+            append("${originalType.annotations.toList()} ")
+        }
+        append(newType.declaration.simpleName.asString())
+        if (arguments.isNotEmpty()) {
+            append("$arguments")
+        }
+    }
+
+    private companion object {
+        fun KSType.replaceTypeAliases() = (declaration as? KSTypeAlias)?.type?.resolve() ?: this
+
+        fun KSType.replaceSuspendFunctionTypes(resolver: Resolver) = if (!isSuspendFunctionType) {
+            this
+        } else {
+            // Find the JVM FunctionN type that will replace the suspend function and use that.
+            val functionN = resolver.requireType(
+                (declaration.asJTypeName(resolver).rawTypeName() as JClassName).canonicalName()
+            )
+            functionN.replace(
+                buildList {
+                    addAll(arguments.dropLast(1))
+                    val continuationArgs = arguments.takeLast(1)
+                    val continuationTypeRef = resolver.requireType("kotlin.coroutines.Continuation")
+                        .replace(continuationArgs)
+                        .createTypeReference()
+                    val objTypeRef = resolver.requireType("java.lang.Object").createTypeReference()
+                    add(resolver.getTypeArgument(continuationTypeRef, Variance.INVARIANT))
+                    add(resolver.getTypeArgument(objTypeRef, Variance.INVARIANT))
+                }
+            )
+        }
     }
 }
 
@@ -480,36 +367,69 @@
  * we'll lose information about annotations (e.g. `@JvmSuppressWildcards`) that were on the original
  * type argument.
  */
-private class KSTypeArgumentWrapper(
-    val delegate: KSTypeArgument,
-    override val type: KSTypeReference,
-    override val variance: Variance,
-) : KSTypeArgument by delegate {
-    override fun toString() = when (variance) {
-        Variance.INVARIANT -> "${type.resolve()}"
-        Variance.CONTRAVARIANT -> "in ${type.resolve()}"
-        Variance.COVARIANT -> "out ${type.resolve()}"
-        Variance.STAR -> "*"
-    }
-}
-
-/**
- * Inheriting variance for self referencing types (e.g. Foo<T : Foo>) could go into an infinite
- * loop. To avoid that issue, every time we visit a type, we keep it in the reference stack and
- * if a type argument resolves to it, it will stop recursion.
- */
-private class ReferenceStack {
-    val queue = ArrayDeque<KSType>()
-
-    inline fun <T> withReference(
-        ksType: KSType,
-        crossinline block: () -> T
-    ): T {
-        return try {
-            queue.addLast(ksType)
-            block()
-        } finally {
-            queue.removeLast()
+private class KSTypeArgumentWrapper constructor(
+    private val originalTypeArg: KSTypeArgument,
+    private val newType: KSTypeWrapper? = null,
+    private val resolver: Resolver,
+    val typeParam: KSTypeParameter,
+    val variance: Variance = originalTypeArg.variance,
+    val typeStack: List<KSTypeWrapper>,
+    val typeArgStack: List<KSTypeArgumentWrapper>,
+    val typeParamStack: List<KSTypeParameter>,
+) {
+    val type: KSTypeWrapper? by lazy {
+        if (variance == Variance.STAR || originalTypeArg.type == null) {
+            // Return null for star projections, otherwise we'll end up in an infinite loop.
+            null
+        } else {
+            newType ?: KSTypeWrapper(
+                originalType = originalTypeArg.type!!.resolve(),
+                resolver = resolver,
+                typeStack = typeStack,
+                typeArgStack = typeArgStack + this,
+                typeParamStack = typeParamStack + typeParam,
+            )
         }
     }
+
+    fun replace(newType: KSTypeWrapper, newVariance: Variance) = KSTypeArgumentWrapper(
+        originalTypeArg = originalTypeArg,
+        typeParam = typeParam,
+        newType = newType,
+        variance = newVariance,
+        resolver = resolver,
+        typeStack = typeStack,
+        typeArgStack = typeArgStack,
+        typeParamStack = typeParamStack,
+    )
+
+    fun hasJvmWildcardAnnotation() = originalTypeArg.hasJvmWildcardAnnotation()
+
+    fun hasSuppressJvmWildcardAnnotation() = originalTypeArg.hasSuppressJvmWildcardAnnotation()
+
+    fun hasSuppressWildcardsAnnotationInHierarchy() =
+        originalTypeArg.hasSuppressWildcardsAnnotationInHierarchy()
+
+    fun unwrap(): KSTypeArgument {
+        val unwrappedType = type?.unwrap()
+        return if (unwrappedType == null || unwrappedType.isError) {
+            originalTypeArg
+        } else {
+            resolver.getTypeArgument(unwrappedType.createTypeReference(), variance)
+        }
+    }
+
+    override fun toString() = buildString {
+        if (originalTypeArg.annotations.toList().isNotEmpty()) {
+            append("${originalTypeArg.annotations.toList()} ")
+        }
+        append(
+            when (variance) {
+                Variance.INVARIANT -> "$type"
+                Variance.CONTRAVARIANT -> "in $type"
+                Variance.COVARIANT -> "out $type"
+                Variance.STAR -> "*"
+            }
+        )
+    }
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt
index 19f546a..a124660 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt
@@ -36,15 +36,18 @@
      * parameter is in kotlin or the containing class, which inherited the method, is in kotlin.
      */
     val needsWildcardResolution: Boolean by lazy {
-        fun nodeForSuppressionCheck(): KSAnnotated? = when (this) {
+        annotated.isInKotlinCode() || container?.isInKotlinCode() == true
+    }
+
+    val hasSuppressWildcards: Boolean by lazy {
+        val nodeForSuppressionCheck = when (this) {
             // For property setter and getter methods skip to the enclosing class to check for
             // suppression annotations to match KAPT.
             is PropertySetterParameterType,
             is PropertyGetterMethodReturnType -> annotated.parent?.parent as? KSAnnotated
             else -> annotated
         }
-        (annotated.isInKotlinCode() || container?.isInKotlinCode() == true) &&
-            nodeForSuppressionCheck()?.hasSuppressWildcardsAnnotationInHierarchy() != true
+        nodeForSuppressionCheck?.hasSuppressWildcardsAnnotationInHierarchy() == true
     }
 
     private fun KSAnnotated.isInKotlinCode(): Boolean {
@@ -67,7 +70,7 @@
     abstract fun isValOrReturnType(): Boolean
 
     /** Returns the scope of the `asMemberOf` container type. */
-    fun asMemberOfScopeOrSelf(): KSTypeVarianceResolverScope? {
+    fun asMemberOfScopeOrSelf(): KSTypeVarianceResolverScope {
         return asMemberOf?.scope ?: this
     }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
index acf2615..ae5a43cb 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
@@ -295,7 +295,7 @@
     /**
      * Resolves the wildcards for the given ksType. See [KSTypeVarianceResolver] for details.
      */
-    internal fun resolveWildcards(ksType: KSType, scope: KSTypeVarianceResolverScope) =
+    internal fun resolveWildcards(ksType: KSType, scope: KSTypeVarianceResolverScope?) =
         ksTypeVarianceResolver.applyTypeVariance(ksType, scope)
 
     internal fun clearCache() {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index 74709b2..dc5cf7c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -69,23 +69,19 @@
      * The [XTypeName] represents those differences as [JTypeName] and [KTypeName], respectively.
      */
     private val xTypeName: XTypeName by lazy {
-        val jvmWildcardType = if (scope == null) {
-            this
-        } else {
-            env.resolveWildcards(ksType, scope).let {
-                if (it == ksType) {
-                    this
-                } else {
-                    env.wrap(
-                        ksType = it,
-                        allowPrimitives = this is KspPrimitiveType
-                    ).copyWithScope(scope)
-                }
+        val jvmWildcardType = env.resolveWildcards(ksType, scope).let {
+            if (it == ksType) {
+                this
+            } else {
+                env.wrap(
+                    ksType = it,
+                    allowPrimitives = this is KspPrimitiveType
+                )
             }
         }
         XTypeName(
             jvmWildcardType.resolveJTypeName(),
-            jvmWildcardType.resolveKTypeName(),
+            resolveKTypeName(),
             nullability
         )
     }
@@ -141,15 +137,12 @@
                 if (argDeclaration is KSTypeParameter) {
                     // If this is a type parameter, replace it with the resolved type argument.
                     resolvedTypeArguments[argDeclaration.name.asString()] ?: argument
-                } else if (
-                    argument.type != null && argument.type?.resolve()?.arguments?.isEmpty() == false
-                ) {
+                } else if (argument.type?.resolve()?.arguments?.isEmpty() == false) {
                     // If this is a type with arguments, the arguments may contain a type parameter,
                     // e.g. Foo<T>, so try to resolve the type and then convert to a type argument.
                     env.resolver.getTypeArgument(
-                        env.resolver.createKSTypeReferenceFromKSType(
-                            resolveTypeArguments(argument.type!!.resolve(), resolvedTypeArguments)
-                        ),
+                        resolveTypeArguments(argument.type!!.resolve(), resolvedTypeArguments)
+                            .createTypeReference(),
                         variance = Variance.INVARIANT
                     )
                 } else {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeArgumentType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeArgumentType.kt
index 7593b7f..589244d 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeArgumentType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeArgumentType.kt
@@ -21,6 +21,7 @@
 import com.google.devtools.ksp.symbol.KSTypeArgument
 import com.google.devtools.ksp.symbol.KSTypeParameter
 import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Variance
 import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.KTypeName
 
@@ -47,7 +48,8 @@
             ksType = ksType,
             allowPrimitives = false
         )
-        if (this.ksType.declaration is KSTypeParameter && this == extendBound) {
+        if (typeArg.variance == Variance.STAR ||
+            (this.ksType.declaration is KSTypeParameter && this == extendBound)) {
             null
         } else {
             extendBound
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
index ba7cb80..f09dcbd 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
@@ -32,7 +32,6 @@
 import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
 import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.JWildcardTypeName
-import com.squareup.kotlinpoet.javapoet.KWildcardTypeName
 import kotlin.coroutines.Continuation
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -94,8 +93,7 @@
                     )
                     if (invocation.isKsp) {
                         assertThat(it.asTypeName().kotlin).isEqualTo(
-                            Continuation::class.asKClassName()
-                                .parameterizedBy(KWildcardTypeName.consumerOf(LONG))
+                            Continuation::class.asKClassName().parameterizedBy(LONG)
                         )
                     }
                 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
index f337713..e9b693c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
@@ -64,20 +64,13 @@
         fieldName: String,
         expectedJTypeName: String,
     ) {
-        val expectedKTypeName = expectedJTypeName.replace("? extends", "out")
         val sub = processingEnv.requireTypeElement("SubClass")
         val subField = sub.getField(fieldName).asMemberOf(sub.type)
         assertThat(subField.asTypeName().java.toString()).isEqualTo(expectedJTypeName)
-        if (isKsp) {
-            assertThat(subField.asTypeName().kotlin.toString()).isEqualTo(expectedKTypeName)
-        }
 
         val base = processingEnv.requireTypeElement("BaseClass")
         val baseField = base.getField(fieldName).asMemberOf(sub.type)
         assertThat(baseField.asTypeName().java.toString()).isEqualTo(expectedJTypeName)
-        if (isKsp) {
-            assertThat(baseField.asTypeName().kotlin.toString()).isEqualTo(expectedKTypeName)
-        }
     }
 
     private fun XTestInvocation.assertParamType(
@@ -85,23 +78,16 @@
         paramName: String,
         expectedJTypeName: String,
     ) {
-        val expectedKTypeName = expectedJTypeName.replace("? extends", "out")
         val sub = processingEnv.requireTypeElement("SubClass")
         val subMethod = sub.getMethodByJvmName(methodName)
         val paramIndex = subMethod.parameters.indexOf(subMethod.getParameter(paramName))
         val subMethodParam = subMethod.asMemberOf(sub.type).parameterTypes[paramIndex]
         assertThat(subMethodParam.asTypeName().java.toString()).isEqualTo(expectedJTypeName)
-        if (isKsp) {
-            assertThat(subMethodParam.asTypeName().kotlin.toString()).isEqualTo(expectedKTypeName)
-        }
 
         val base = processingEnv.requireTypeElement("BaseClass")
         val baseMethod = base.getMethodByJvmName(methodName)
         val baseMethodParam = baseMethod.asMemberOf(sub.type).parameterTypes[paramIndex]
         assertThat(baseMethodParam.asTypeName().java.toString()).isEqualTo(expectedJTypeName)
-        if (isKsp) {
-            assertThat(baseMethodParam.asTypeName().kotlin.toString()).isEqualTo(expectedKTypeName)
-        }
     }
 
     private fun XTestInvocation.assertReturnType(methodName: String, expectedTypeName: String) {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 0e44884..6dde1fd 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -2075,272 +2075,6 @@
         }
     }
 
-    @Test
-    fun javaFieldDescriptors() {
-        runTest(
-            sources = listOf(
-                Source.java(
-                    "TestClassA",
-                    """
-                    import java.util.List;
-                    class TestClassA<T> {
-                        int field1;
-                        String field2;
-                        T field3;
-                        List<String> field4;
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassA")
-            assertThat(foo.getDeclaredFields().map { it.jvmDescriptor }.toList())
-                .containsExactly(
-                    "field1:I",
-                    "field2:Ljava/lang/String;",
-                    "field3:Ljava/lang/Object;",
-                    "field4:Ljava/util/List;"
-                )
-        }
-    }
-
-    @Test
-    fun javaMethodDescriptorsPrimitives() {
-        runTest(
-            sources = listOf(
-                Source.java(
-                    "TestClassB",
-                    """
-                    class TestClassB<T> {
-                        void method1(boolean yesOrNo, int number) {}
-
-                        byte method2(char letter) {
-                          return 0;
-                        }
-
-                        void method3(double realNumber1, float realNummber2) {}
-
-                        void method4(long bigNumber, short littlerNumber) {}
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassB")
-            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
-                .containsExactly(
-                    "method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V"
-                )
-        }
-    }
-
-    @Test
-    fun javaMethodDescriptorsJavaTypes() {
-        runTest(
-            sources = listOf(
-                Source.java(
-                    "TestClassC",
-                    """
-                    import java.util.*;
-                    class TestClassC<T> {
-                        void method1(Object something) {}
-
-                        Object method2() {
-                          return null;
-                        }
-
-                        List<String> method3(ArrayList<Integer> list) {
-                          return null;
-                        }
-
-                        Map<String, Object> method4() {
-                          return null;
-                        }
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassC")
-            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
-                .containsExactly(
-                    "method1(Ljava/lang/Object;)V",
-                    "method2()Ljava/lang/Object;",
-                    "method3(Ljava/util/ArrayList;)Ljava/util/List;",
-                    "method4()Ljava/util/Map;"
-                )
-        }
-    }
-
-    @Test
-    fun javaMethodDescriptorsTestTypes() {
-        runTest(
-            sources = listOf(
-                Source.java(
-                    "TestClassD",
-                    """
-                    class TestDataClass {}
-                    class TestClassD<T> {
-                        void method1(TestDataClass data) {}
-
-                        TestDataClass method2() {
-                          return null;
-                        }
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassD")
-            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
-                .containsExactly(
-                    "method1(LTestDataClass;)V",
-                    "method2()LTestDataClass;"
-                )
-        }
-    }
-
-    @Test
-    fun javaMethodDescriptorsArrays() {
-        runTest(
-            sources = listOf(
-                Source.java(
-                    "TestClassE",
-                    """
-                    class TestDataClass {}
-                    class TestClassE<T> {
-                        void method1(TestDataClass[] data) {}
-
-                        TestDataClass[] method2() {
-                          return null;
-                        }
-
-                        void method3(int[] array) {}
-
-                        void method4(int... array) {}
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassE")
-            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
-                .containsExactly(
-                    "method1([LTestDataClass;)V",
-                    "method2()[LTestDataClass;",
-                    "method3([I)V",
-                    "method4([I)V"
-                )
-        }
-    }
-
-    @Test
-    fun javaMethodDescriptorsInnerTestType() {
-        runTest(
-            // KSP can't see nested types if the filename does not match the name of the
-            // enclosing class.
-            sources = listOf(
-                Source.java(
-                    "TestDataClass",
-                    """
-                    public class TestDataClass {
-                        class MemberInnerData {}
-
-                        static class StaticInnerData {}
-
-                        enum EnumData {
-                          VALUE1,
-                          VALUE2
-                        }
-                    }
-                    """.trimIndent()
-                ),
-                Source.java(
-                    "TestClassF",
-                    """
-                    class TestClassF<T> {
-                        void method1(TestDataClass.MemberInnerData data) {}
-
-                        void method2(TestDataClass.StaticInnerData data) {}
-
-                        void method3(TestDataClass.EnumData enumData) {}
-
-                        TestDataClass.StaticInnerData method4() {
-                          return null;
-                        }
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassF")
-            assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
-                .containsExactly(
-                    "method1(LTestDataClass\$MemberInnerData;)V",
-                    "method2(LTestDataClass\$StaticInnerData;)V",
-                    "method3(LTestDataClass\$EnumData;)V",
-                    "method4()LTestDataClass\$StaticInnerData;"
-                )
-        }
-    }
-
-    @Test
-    fun methodDescriptorsErasure() {
-        runTest(
-            sources = listOf(
-                Source.java(
-                    "TestClassG",
-                    """
-                    import java.util.*;
-                    class TestClassG<T> {
-                        void method1(T something) {}
-                        T method2() {
-                          return null;
-                        }
-                        List<? extends String> method3() {
-                          return null;
-                        }
-                        Map<T, String> method4() {
-                          return null;
-                        }
-                        ArrayList<Map<T, String>> method5() {
-                          return null;
-                        }
-                        static <I, O extends I> O method6(I input) {
-                          return null;
-                        }
-                        static <I, O extends String> O method7(I input) {
-                          return null;
-                        }
-                        static <P extends Collection<String> & Comparable<String>> P method8() {
-                          return null;
-                        }
-                        static <P extends String & List<Character>> P method9() {
-                          return null;
-                        }
-                    }
-                    """.trimIndent()
-                )
-            )
-        ) { invocation ->
-            val foo = invocation.processingEnv.requireTypeElement("TestClassG")
-            if (!invocation.isKsp) {
-                assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
-                    .containsExactly(
-                        "method1(Ljava/lang/Object;)V",
-                        "method2()Ljava/lang/Object;",
-                        "method3()Ljava/util/List;",
-                        "method4()Ljava/util/Map;",
-                        "method5()Ljava/util/ArrayList;",
-                        "method6(Ljava/lang/Object;)Ljava/lang/Object;",
-                        "method7(Ljava/lang/Object;)Ljava/lang/String;",
-                        "method8()Ljava/util/Collection;",
-                        "method9()Ljava/lang/String;"
-                    )
-            }
-        }
-    }
-
     /**
      * it is good to exclude methods coming from Object when testing as they differ between KSP
      * and KAPT but irrelevant for Room.
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 724c037..12b61d7 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -933,7 +933,7 @@
                 assertThat(actual["starList"]?.kotlin.toString())
                     .isEqualTo("kotlin.collections.List<*>")
                 assertThat(actual["rList"]?.kotlin.toString())
-                    .isEqualTo("kotlin.collections.List<out R>")
+                    .isEqualTo("kotlin.collections.List<R>")
             }
         }
     }
@@ -1637,4 +1637,56 @@
             }
         )
     }
+
+    @Test
+    fun selfReferenceTypesDoesNotInfinitelyRecurse() {
+        fun runTest(src: Source) {
+            runProcessorTest(
+                sources = listOf(src),
+            ) { invocation ->
+                val fooTypeElement = invocation.processingEnv.requireTypeElement("test.Usage")
+                val fooType = fooTypeElement.getDeclaredField("foo").type
+
+                assertThat(fooType.asTypeName().java)
+                    .isEqualTo(
+                        JParameterizedTypeName.get(
+                            JClassName.get("test", "Foo"),
+                            JWildcardTypeName.subtypeOf(JClassName.OBJECT)
+                        )
+                    )
+
+                val typeParam = fooType.typeArguments.single()
+                assertThat(typeParam.asTypeName().java)
+                    .isEqualTo(JWildcardTypeName.subtypeOf(JClassName.OBJECT))
+
+                assertThat(typeParam.extendsBound()).isNull()
+            }
+        }
+        runTest(
+            Source.java(
+                "test.Usage",
+                """
+                package test;
+                public final class Usage {
+                  private final Foo<?> foo = null;
+                  private final Foo<? extends Foo<?>> fooExtendsFoo = null;
+                }
+                abstract class Foo<T extends Foo<T>> {}
+                """.trimIndent()
+            ),
+        )
+        runTest(
+            Source.kotlin(
+                "test.Foo.kt",
+                """
+            package test
+            class Usage {
+                val foo: Foo<*> = TODO()
+                val fooExtendsFoo: Foo<out Foo<*>> = TODO()
+            }
+            abstract class Foo<T: Foo<T>>
+            """.trimIndent()
+            )
+        )
+    }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
index 16b8f8c..0fb042c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
@@ -91,17 +91,13 @@
                 """.trimIndent()
             )
             val kaptSignaturesMap = buildMap(sourcesMap.size) {
-                runKaptTest(sources) {
-                    sourcesMap.keys.forEach { key ->
-                        put(key, IntRange(0, 5).flatMap { i -> collectSignatures(it, key, i) })
-                    }
+                runKaptTest(sources) { invocation ->
+                    sourcesMap.keys.forEach { key -> put(key, invocation.collectSignatures(key)) }
                 }
             }
             val kspSignaturesMap = buildMap(sourcesMap.size) {
-                runKspTest(sources) {
-                    sourcesMap.keys.forEach { key ->
-                        put(key, IntRange(0, 5).flatMap { i -> collectSignatures(it, key, i) })
-                    }
+                runKspTest(sources) { invocation ->
+                    sourcesMap.keys.forEach { key -> put(key, invocation.collectSignatures(key)) }
                 }
             }
             CompilationResults(kaptSignaturesMap, kspSignaturesMap)
@@ -123,48 +119,47 @@
             )
         }
 
-        private fun collectSignatures(
-            invocation: XTestInvocation,
-            key: String,
-            configuration: Int,
-        ): List<String> {
-            val subName = "Sub$key$configuration"
-            val baseName = "Base$key$configuration"
-            val sub = invocation.processingEnv.requireTypeElement(subName)
-            val subMethod = sub.getDeclaredMethodByJvmName("subMethod")
-            val subSuperclassType = sub.superClass!!
-            val subMethodParamType = subMethod.parameters.single().type
-            val subMethodReturnType = subMethod.returnType
-            val base = invocation.processingEnv.requireTypeElement(baseName)
-            // Note: For each method/field we test its signature when resolved asMemberOf from a
-            // subtype, super class, param type, and return type, as we may get different signatures
-            // depending on the scope of the type used with asMemberOf.
+        private fun XTestInvocation.collectSignatures(key: String): List<String> {
             return buildList {
-                base.getDeclaredMethods().forEach { method ->
-                    fun XMethodType.signature(): String {
-                        val returnType = returnType.typeName
-                        val parameters = parameterTypes.map { it.typeName }
-                        return "${method.name} : $returnType : $parameters"
+                IntRange(0, 5).forEach { configuration ->
+                    val subName = "Sub$key$configuration"
+                    val baseName = "Base$key$configuration"
+                    val sub = processingEnv.requireTypeElement(subName)
+                    val subMethod = sub.getDeclaredMethodByJvmName("subMethod")
+                    val subSuperclassType = sub.superClass!!
+                    val subMethodParamType = subMethod.parameters.single().type
+                    val subMethodReturnType = subMethod.returnType
+                    val base = processingEnv.requireTypeElement(baseName)
+                    // Note: For each method/field we test its signature when resolved asMemberOf from a
+                    // subtype, super class, param type, and return type, as we may get different signatures
+                    // depending on the scope of the type used with asMemberOf.
+                    base.getDeclaredMethods().forEach { method ->
+                        fun XMethodType.signature(): String {
+                            val returnType = returnType.typeName
+                            val parameters = parameterTypes.map { it.typeName }
+                            return "${method.name} : $returnType : $parameters"
+                        }
+
+                        val fromSubType = method.asMemberOf(sub.type)
+                        val fromSuperClassType = method.asMemberOf(subSuperclassType)
+                        val fromParamType = method.asMemberOf(subMethodParamType)
+                        val fromReturnType = method.asMemberOf(subMethodReturnType)
+                        add("$configuration-fromSub-${fromSubType.signature()}")
+                        add("$configuration-fromSuperClass-${fromSuperClassType.signature()}")
+                        add("$configuration-fromParam-${fromParamType.signature()}")
+                        add("$configuration-fromReturnType-${fromReturnType.signature()}")
                     }
-                    val fromSubType = method.asMemberOf(sub.type)
-                    val fromSuperClassType = method.asMemberOf(subSuperclassType)
-                    val fromParamType = method.asMemberOf(subMethodParamType)
-                    val fromReturnType = method.asMemberOf(subMethodReturnType)
-                    add("$configuration-fromSub-${fromSubType.signature()}")
-                    add("$configuration-fromSuperClass-${fromSuperClassType.signature()}")
-                    add("$configuration-fromParam-${fromParamType.signature()}")
-                    add("$configuration-fromReturnType-${fromReturnType.signature()}")
-                }
-                base.getDeclaredFields().forEach { field ->
-                    fun XType.signature() = "${field.name} : $typeName"
-                    val fromSubType = field.asMemberOf(sub.type)
-                    val fromSuperClassType = field.asMemberOf(subSuperclassType)
-                    val fromParamType = field.asMemberOf(subMethodParamType)
-                    val fromReturnType = field.asMemberOf(subMethodReturnType)
-                    add("$configuration-fromSub-${fromSubType.signature()}")
-                    add("$configuration-fromSuperClass-${fromSuperClassType.signature()}")
-                    add("$configuration-fromParam-${fromParamType.signature()}")
-                    add("$configuration-fromReturnType-${fromReturnType.signature()}")
+                    base.getDeclaredFields().forEach { field ->
+                        fun XType.signature() = "${field.name} : $typeName"
+                        val fromSubType = field.asMemberOf(sub.type)
+                        val fromSuperClassType = field.asMemberOf(subSuperclassType)
+                        val fromParamType = field.asMemberOf(subMethodParamType)
+                        val fromReturnType = field.asMemberOf(subMethodReturnType)
+                        add("$configuration-fromSub-${fromSubType.signature()}")
+                        add("$configuration-fromSuperClass-${fromSuperClassType.signature()}")
+                        add("$configuration-fromParam-${fromParamType.signature()}")
+                        add("$configuration-fromReturnType-${fromReturnType.signature()}")
+                    }
                 }
             }
         }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
index 2e3bd5f..de768e6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
@@ -17,19 +17,25 @@
 package androidx.room.compiler.processing.ksp
 
 import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.isConstructor
 import androidx.room.compiler.processing.isField
 import androidx.room.compiler.processing.isMethod
+import androidx.room.compiler.processing.isTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import org.junit.runners.Parameterized
 
-@RunWith(JUnit4::class)
-class KspJvmDescriptorUtilsTest {
+@RunWith(Parameterized::class)
+class KspJvmDescriptorUtilsTest(
+    private val isPreCompiled: Boolean
+) {
     private val describeAnnotation =
         Source.java(
             "androidx.room.test.Describe",
@@ -487,15 +493,55 @@
         )
     }
 
-    private fun runTest(vararg sources: Source, handler: (XTestInvocation) -> Unit) {
-        runProcessorTest(
-            sources = sources.toList() + describeAnnotation,
-            handler = handler
-        )
+    private fun runTest(
+        vararg sources: Source,
+        handler: (XTestInvocation) -> Unit
+    ) {
+        if (isPreCompiled) {
+            val compiled = compileFiles(listOf(*sources) + describeAnnotation)
+            val hasKotlinSources = sources.any {
+                it is Source.KotlinSource
+            }
+            val kotlinSources = if (hasKotlinSources) {
+                listOf(
+                    Source.kotlin("placeholder.kt", "class PlaceholderKotlin")
+                )
+            } else {
+                emptyList()
+            }
+            val newSources = kotlinSources + Source.java(
+                "PlaceholderJava",
+                "public class " +
+                    "PlaceholderJava {}"
+            )
+            runProcessorTest(
+                sources = newSources,
+                handler = handler,
+                classpath = compiled
+            )
+        } else {
+            runProcessorTest(
+                sources = listOf(*sources) + describeAnnotation,
+                handler = handler
+            )
+        }
     }
 
     private fun XTestInvocation.annotatedElements(): Set<XElement> {
-        return roundEnv.getElementsAnnotatedWith("androidx.room.test.Describe")
+        // RoundEnv.getElementsAnnotatedWith() only processes current round and could not see
+        // precompiled classes.
+        val typeElements = processingEnv.getTypeElementsFromPackage("androidx.room.test")
+        return typeElements
+            .flatMap {
+                it.getElementsAnnotatedWith(ClassName.get(
+                "androidx.room.test", "Describe"))
+            }.toSet()
+    }
+
+    private fun XTypeElement.getElementsAnnotatedWith(annotation: ClassName): Set<XElement> {
+        return (getEnclosedElements().filter { !it.isTypeElement() } + this)
+            .filter { it.hasAnnotation(annotation) }
+            .toSet() + getEnclosedTypeElements().flatMap { it.getElementsAnnotatedWith(annotation) }
     }
 
     private fun descriptor(element: XElement): String {
@@ -506,4 +552,12 @@
             else -> error("Unsupported element to describe.")
         }
     }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "isPreCompiled_{0}")
+        fun params(): List<Array<Any>> {
+            return listOf(arrayOf(false), arrayOf(true))
+        }
+    }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index be3f4bf..35f0b23 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -162,6 +162,20 @@
                 interface MyInterface
                 typealias MyInterfaceAlias = MyInterface
                 typealias MyGenericAlias = MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>
+                typealias MyLambdaAlias1 = (List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MyLambdaAlias2 = @JSW (List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MyLambdaAlias3 = (@JSW List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> @JSW List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MyLambdaAlias4 = (List<@JSW MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> List<@JSW MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MyLambdaAlias5 = (List<MyGenericIn<@JSW MyGenericOut<MyGenericOut<MyType>>>>) -> List<MyGenericIn<@JSW MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MyLambdaAlias6 = (List<MyGenericIn<MyGenericOut<@JSW MyGenericOut<MyType>>>>) -> List<MyGenericIn<MyGenericOut<@JSW MyGenericOut<MyType>>>>
+                typealias MyLambdaAlias7 = (List<MyGenericIn<MyGenericOut<MyGenericOut<@JSW MyType>>>>) -> List<MyGenericIn<MyGenericOut<MyGenericOut<@JSW MyType>>>>
+                typealias MySuspendLambdaAlias1 = suspend (List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MySuspendLambdaAlias2 = @JSW suspend (List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MySuspendLambdaAlias3 = suspend (@JSW List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> @JSW List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MySuspendLambdaAlias4 = suspend (List<@JSW MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>) -> List<@JSW MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MySuspendLambdaAlias5 = suspend (List<MyGenericIn<@JSW MyGenericOut<MyGenericOut<MyType>>>>) -> List<MyGenericIn<@JSW MyGenericOut<MyGenericOut<MyType>>>>
+                typealias MySuspendLambdaAlias6 = suspend (List<MyGenericIn<MyGenericOut<@JSW MyGenericOut<MyType>>>>) -> List<MyGenericIn<MyGenericOut<@JSW MyGenericOut<MyType>>>>
+                typealias MySuspendLambdaAlias7 = suspend (List<MyGenericIn<MyGenericOut<MyGenericOut<@JSW MyType>>>>) -> List<MyGenericIn<MyGenericOut<MyGenericOut<@JSW MyType>>>>
                 typealias JSW = JvmSuppressWildcards
                 typealias JW = JvmWildcard
                 typealias MyLambdaTypeAlias = (@JvmWildcard MyType) -> @JvmWildcard MyType
@@ -453,6 +467,34 @@
                     fun explicitJvmSuppressWildcardTypeAlias_OnType2(
                         list: @JSW List<Number>
                     ): @JSW List<Number> { TODO() }
+                    fun lambda1(param: MyLambdaAlias1): MyLambdaAlias1 = TODO()
+                    fun lambda2(param: MyLambdaAlias2): MyLambdaAlias2 = TODO()
+                    fun lambda3(param: MyLambdaAlias3): MyLambdaAlias3 = TODO()
+                    fun lambda4(param: MyLambdaAlias4): MyLambdaAlias4 = TODO()
+                    fun lambda5(param: MyLambdaAlias5): MyLambdaAlias5 = TODO()
+                    fun lambda6(param: MyLambdaAlias6): MyLambdaAlias6 = TODO()
+                    fun lambda7(param: MyLambdaAlias7): MyLambdaAlias7 = TODO()
+                    @JSW fun lambda1WithJSW(param: MyLambdaAlias1): MyLambdaAlias1 = TODO()
+                    @JSW fun lambda2WithJSW(param: MyLambdaAlias2): MyLambdaAlias2 = TODO()
+                    @JSW fun lambda3WithJSW(param: MyLambdaAlias3): MyLambdaAlias3 = TODO()
+                    @JSW fun lambda4WithJSW(param: MyLambdaAlias4): MyLambdaAlias4 = TODO()
+                    @JSW fun lambda5WithJSW(param: MyLambdaAlias5): MyLambdaAlias5 = TODO()
+                    @JSW fun lambda6WithJSW(param: MyLambdaAlias6): MyLambdaAlias6 = TODO()
+                    @JSW fun lambda7WithJSW(param: MyLambdaAlias7): MyLambdaAlias7 = TODO()
+                    fun suspendLambda1(param: MySuspendLambdaAlias1): MySuspendLambdaAlias1 = TODO()
+                    fun suspendLambda2(param: MySuspendLambdaAlias2): MySuspendLambdaAlias2 = TODO()
+                    fun suspendLambda3(param: MySuspendLambdaAlias3): MySuspendLambdaAlias3 = TODO()
+                    fun suspendLambda4(param: MySuspendLambdaAlias4): MySuspendLambdaAlias4 = TODO()
+                    fun suspendLambda5(param: MySuspendLambdaAlias5): MySuspendLambdaAlias5 = TODO()
+                    fun suspendLambda6(param: MySuspendLambdaAlias6): MySuspendLambdaAlias6 = TODO()
+                    fun suspendLambda7(param: MySuspendLambdaAlias7): MySuspendLambdaAlias7 = TODO()
+                    @JSW fun suspendLambda1WithJSW(param: MySuspendLambdaAlias1): MySuspendLambdaAlias1 = TODO()
+                    @JSW fun suspendLambda2WithJSW(param: MySuspendLambdaAlias2): MySuspendLambdaAlias2 = TODO()
+                    @JSW fun suspendLambda3WithJSW(param: MySuspendLambdaAlias3): MySuspendLambdaAlias3 = TODO()
+                    @JSW fun suspendLambda4WithJSW(param: MySuspendLambdaAlias4): MySuspendLambdaAlias4 = TODO()
+                    @JSW fun suspendLambda5WithJSW(param: MySuspendLambdaAlias5): MySuspendLambdaAlias5 = TODO()
+                    @JSW fun suspendLambda6WithJSW(param: MySuspendLambdaAlias6): MySuspendLambdaAlias6 = TODO()
+                    @JSW fun suspendLambda7WithJSW(param: MySuspendLambdaAlias7): MySuspendLambdaAlias7 = TODO()
                 }
                 """.trimIndent()
             ), listOf(className)
diff --git a/savedstate/savedstate-ktx/src/main/AndroidManifest.xml b/savedstate/savedstate-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index ee19b21..0000000
--- a/savedstate/savedstate-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/settings.gradle b/settings.gradle
index 9abd3a2..f0129a6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -503,6 +503,7 @@
 includeProject(":camera:camera-video", [BuildType.CAMERA])
 includeProject(":camera:camera-view", [BuildType.CAMERA])
 includeProject(":camera:camera-viewfinder", [BuildType.CAMERA])
+includeProject(":camera:camera-viewfinder-compose", [BuildType.CAMERA])
 includeProject(":camera:camera-viewfinder-core", [BuildType.CAMERA])
 includeProject(":camera:integration-tests:camera-testapp-avsync", "camera/integration-tests/avsynctestapp", [BuildType.CAMERA])
 includeProject(":camera:integration-tests:camera-testapp-camera2-pipe", "camera/integration-tests/camerapipetestapp", [BuildType.CAMERA])
diff --git a/slice/slice-builders-ktx/src/main/AndroidManifest.xml b/slice/slice-builders-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/slice/slice-builders-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/slice/slice-builders/src/main/AndroidManifest.xml b/slice/slice-builders/src/main/AndroidManifest.xml
deleted file mode 100644
index 95c4426..0000000
--- a/slice/slice-builders/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/slice/slice-remotecallback/src/main/AndroidManifest.xml b/slice/slice-remotecallback/src/main/AndroidManifest.xml
deleted file mode 100644
index dcf3758..0000000
--- a/slice/slice-remotecallback/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
- <!-- Copyright (C) 2019 The Android Open Source Project
-
-      Licensed under the Apache License, Version 2.0 (the "License");
-        you may not use this file except in compliance with the License.
-        You may obtain a copy of the License at
-
-             http://www.apache.org/licenses/LICENSE-2.0
-
-        Unless required by applicable law or agreed to in writing, software
-        distributed under the License is distributed on an "AS IS" BASIS,
-        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-        See the License for the specific language governing permissions and
-        limitations under the License.
- -->
- <manifest />
-
diff --git a/slice/slice-view/src/main/AndroidManifest.xml b/slice/slice-view/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/slice/slice-view/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/slidingpanelayout/slidingpanelayout/src/main/AndroidManifest.xml b/slidingpanelayout/slidingpanelayout/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/slidingpanelayout/slidingpanelayout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/sqlite/sqlite-inspection/src/androidTest/AndroidManifest.xml b/sqlite/sqlite-inspection/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e95a361..0000000
--- a/sqlite/sqlite-inspection/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
diff --git a/sqlite/sqlite-inspection/src/main/AndroidManifest.xml b/sqlite/sqlite-inspection/src/main/AndroidManifest.xml
deleted file mode 100644
index e95a361..0000000
--- a/sqlite/sqlite-inspection/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
diff --git a/sqlite/sqlite-ktx/src/main/AndroidManifest.xml b/sqlite/sqlite-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 29b496b..0000000
--- a/sqlite/sqlite-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/swiperefreshlayout/swiperefreshlayout/src/main/AndroidManifest.xml b/swiperefreshlayout/swiperefreshlayout/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/swiperefreshlayout/swiperefreshlayout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/test/screenshot/screenshot/src/main/AndroidManifest.xml b/test/screenshot/screenshot/src/main/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/test/screenshot/screenshot/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
index 54717f6..0e7dd93 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
@@ -59,7 +59,6 @@
 public class UiDeviceTest extends BaseTest {
 
     private static final long TIMEOUT_MS = 5_000;
-    private static final int GESTURE_MARGIN = 50;
     private static final String PACKAGE_NAME = "androidx.test.uiautomator.testapp";
     // Defined in 'AndroidManifest.xml'.
     private static final String APP_NAME = "UiAutomator Test App";
@@ -300,7 +299,6 @@
         assertEquals("I've been clicked!", button.getText());
     }
 
-    @Ignore // b/266617096
     @Test
     public void testSwipe() {
         launchTestActivity(SwipeTestActivity.class);
@@ -309,7 +307,7 @@
 
         int width = mDevice.getDisplayWidth();
         int height = mDevice.getDisplayHeight();
-        mDevice.swipe(GESTURE_MARGIN, height / 2, width - GESTURE_MARGIN, height / 2, 10);
+        mDevice.swipe(width / 10, height / 2, 9 * width / 10, height / 2, 10);
 
         assertTrue(swipeRegion.wait(Until.textEquals("swipe_right"), TIMEOUT_MS));
     }
@@ -330,7 +328,6 @@
         assertTrue(dragDestination.wait(Until.textEquals("drag_received"), TIMEOUT_MS));
     }
 
-    @Ignore // b/266617096
     @Test
     public void testSwipe_withPointArray() {
         launchTestActivity(SwipeTestActivity.class);
@@ -340,9 +337,9 @@
         int width = mDevice.getDisplayWidth();
         int height = mDevice.getDisplayHeight();
 
-        Point point1 = new Point(GESTURE_MARGIN, height / 2);
+        Point point1 = new Point(width / 10, height / 2);
         Point point2 = new Point(width / 2, height / 2);
-        Point point3 = new Point(width - GESTURE_MARGIN, height / 2);
+        Point point3 = new Point(9 * width / 10, height / 2);
 
         mDevice.swipe(new Point[]{point1, point2, point3}, 10);
 
@@ -357,6 +354,7 @@
     }
 
     @Test
+    @Ignore("b/280669851")
     public void testSetOrientationLeft() throws Exception {
         launchTestActivity(KeycodeTestActivity.class);
         try {
@@ -375,6 +373,7 @@
     }
 
     @Test
+    @Ignore("b/280669851")
     public void testSetOrientationRight() throws Exception {
         launchTestActivity(KeycodeTestActivity.class);
         try {
@@ -404,6 +403,7 @@
     }
 
     @Test
+    @Ignore("b/280669851")
     public void testSetOrientationLandscape() throws Exception {
         launchTestActivity(KeycodeTestActivity.class);
         try {
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
index c91c791..bb9f70b 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
@@ -551,7 +551,6 @@
                 + "but got [%f]", scaleValueAfterPinch), scaleValueAfterPinch > 1f);
     }
 
-    @Ignore // b/266617335
     @Test
     public void testSwipe() {
         launchTestActivity(SwipeTestActivity.class);
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
index 36aaeb6..f74a13b 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
@@ -24,12 +24,10 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.uiautomator.UiObject;
 import androidx.test.uiautomator.UiSelector;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class UiObjectTest extends BaseTest {
@@ -119,7 +117,6 @@
         assertTrue(expectedDragDest.waitForExists(TIMEOUT_MS));
     }
 
-    @Ignore // b/266617747
     @Test
     public void testSwipeUp() throws Exception {
         launchTestActivity(SwipeTestActivity.class);
@@ -141,7 +138,6 @@
         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
     }
 
-    @Ignore // b/266617747
     @Test
     public void testSwipeDown() throws Exception {
         launchTestActivity(SwipeTestActivity.class);
@@ -161,7 +157,6 @@
         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
     }
 
-    @FlakyTest(bugId = 242761733)
     @Test
     public void testSwipeLeft() throws Exception {
         launchTestActivity(SwipeTestActivity.class);
@@ -181,7 +176,6 @@
         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
     }
 
-    @Ignore // b/266617747
     @Test
     public void testSwipeRight() throws Exception {
         launchTestActivity(SwipeTestActivity.class);
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiScrollableTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiScrollableTest.java
index 765aab1..3384042 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiScrollableTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiScrollableTest.java
@@ -273,7 +273,6 @@
         assertUiObjectNotFound(noNode::flingBackward);
     }
 
-    @Ignore // b/266965027
     @Test
     public void testScrollBackward_vertical() throws Exception {
         launchTestActivity(SwipeTestActivity.class);
@@ -285,7 +284,6 @@
         assertEquals("swipe_down", scrollRegion.getText());
     }
 
-    @Ignore // b/266965027
     @Test
     public void testScrollBackward_horizontal() throws Exception {
         launchTestActivity(SwipeTestActivity.class);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java
index fda840d..9a1fe18 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java
@@ -22,6 +22,7 @@
 import android.view.MotionEvent;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 public class SwipeTestActivity extends Activity {
@@ -31,23 +32,20 @@
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         setContentView(R.layout.swipe_test_activity);
 
         TextView swipeRegion = findViewById(R.id.swipe_region);
 
         mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
             @Override
-            public boolean onFling(MotionEvent e1, MotionEvent e2, float vX, float vY) {
-                // Swipe is using the same logic as fling, except that their directions are
-                // opposite under the same finger movement.
-                boolean horizontal = Math.abs(vX) > Math.abs(vY);
+            public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+                    float distanceX, float distanceY) {
+                boolean horizontal = Math.abs(distanceX) > Math.abs(distanceY);
                 if (horizontal) {
-                    swipeRegion.setText(vX < 0 ? "swipe_left" : "swipe_right");
+                    swipeRegion.setText(distanceX > 0 ? "swipe_left" : "swipe_right");
                 } else {
-                    swipeRegion.setText(vY < 0 ? "swipe_up" : "swipe_down");
+                    swipeRegion.setText(distanceY > 0 ? "swipe_up" : "swipe_down");
                 }
-
                 return true;
             }
         });
diff --git a/test/uiautomator/uiautomator/src/main/AndroidManifest.xml b/test/uiautomator/uiautomator/src/main/AndroidManifest.xml
deleted file mode 100644
index 6f49e23..0000000
--- a/test/uiautomator/uiautomator/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java
index 63a0a49..b818481 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java
@@ -16,6 +16,8 @@
 
 package androidx.test.uiautomator;
 
+import static java.util.Objects.requireNonNull;
+
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 
@@ -106,8 +108,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector clazz(@NonNull String className) {
-        checkNotNull(className, "className cannot be null");
-
+        requireNonNull(className, "className cannot be null");
         // If className starts with a period, assume the package is 'android.widget'
         if (className.charAt(0) == '.') {
             return clazz("android.widget", className.substring(1));
@@ -126,9 +127,8 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector clazz(@NonNull String packageName, @NonNull String className) {
-        checkNotNull(packageName, "packageName cannot be null");
-        checkNotNull(className, "className cannot be null");
-
+        requireNonNull(packageName, "packageName cannot be null");
+        requireNonNull(className, "className cannot be null");
         return clazz(Pattern.compile(Pattern.quote(
                 String.format("%s.%s", packageName, className))));
     }
@@ -141,8 +141,7 @@
      * @return A reference to this {@link BySelector}
      */
     public @NonNull BySelector clazz(@NonNull Class clazz) {
-        checkNotNull(clazz, "clazz cannot be null");
-
+        requireNonNull(clazz, "clazz cannot be null");
         return clazz(Pattern.compile(Pattern.quote(clazz.getName())));
     }
 
@@ -155,8 +154,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector clazz(@NonNull Pattern className) {
-        checkNotNull(className, "className cannot be null");
-
+        requireNonNull(className, "className cannot be null");
         if (mClazz != null) {
             throw new IllegalStateException("Class selector is already defined");
         }
@@ -173,8 +171,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector desc(@NonNull String contentDescription) {
-        checkNotNull(contentDescription, "contentDescription cannot be null");
-
+        requireNonNull(contentDescription, "contentDescription cannot be null");
         return desc(Pattern.compile(Pattern.quote(contentDescription)));
     }
 
@@ -187,8 +184,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector descContains(@NonNull String substring) {
-        checkNotNull(substring, "substring cannot be null");
-
+        requireNonNull(substring, "substring cannot be null");
         return desc(RegexHelper.getPatternContains(substring));
     }
 
@@ -201,8 +197,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector descStartsWith(@NonNull String substring) {
-        checkNotNull(substring, "substring cannot be null");
-
+        requireNonNull(substring, "substring cannot be null");
         return desc(RegexHelper.getPatternStartsWith(substring));
     }
 
@@ -215,8 +210,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector descEndsWith(@NonNull String substring) {
-        checkNotNull(substring, "substring cannot be null");
-
+        requireNonNull(substring, "substring cannot be null");
         return desc(RegexHelper.getPatternEndsWith(substring));
     }
 
@@ -229,8 +223,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector desc(@NonNull Pattern contentDescription) {
-        checkNotNull(contentDescription, "contentDescription cannot be null");
-
+        requireNonNull(contentDescription, "contentDescription cannot be null");
         if (mDesc != null) {
             throw new IllegalStateException("Description selector is already defined");
         }
@@ -247,8 +240,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector pkg(@NonNull String applicationPackage) {
-        checkNotNull(applicationPackage, "applicationPackage cannot be null");
-
+        requireNonNull(applicationPackage, "applicationPackage cannot be null");
         return pkg(Pattern.compile(Pattern.quote(applicationPackage)));
     }
 
@@ -261,8 +253,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector pkg(@NonNull Pattern applicationPackage) {
-        checkNotNull(applicationPackage, "applicationPackage cannot be null");
-
+        requireNonNull(applicationPackage, "applicationPackage cannot be null");
         if (mPkg != null) {
             throw new IllegalStateException("Package selector is already defined");
         }
@@ -279,8 +270,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector res(@NonNull String resourceName) {
-        checkNotNull(resourceName, "resourceName cannot be null");
-
+        requireNonNull(resourceName, "resourceName cannot be null");
         return res(Pattern.compile(Pattern.quote(resourceName)));
     }
 
@@ -294,9 +284,8 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector res(@NonNull String resourcePackage, @NonNull String resourceId) {
-        checkNotNull(resourcePackage, "resourcePackage cannot be null");
-        checkNotNull(resourceId, "resourceId cannot be null");
-
+        requireNonNull(resourcePackage, "resourcePackage cannot be null");
+        requireNonNull(resourceId, "resourceId cannot be null");
         return res(Pattern.compile(Pattern.quote(
                 String.format("%s:id/%s", resourcePackage, resourceId))));
     }
@@ -310,8 +299,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector res(@NonNull Pattern resourceName) {
-        checkNotNull(resourceName, "resourceName cannot be null");
-
+        requireNonNull(resourceName, "resourceName cannot be null");
         if (mRes != null) {
             throw new IllegalStateException("Resource name selector is already defined");
         }
@@ -328,8 +316,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector text(@NonNull String textValue) {
-        checkNotNull(textValue, "textValue cannot be null");
-
+        requireNonNull(textValue, "textValue cannot be null");
         return text(Pattern.compile(Pattern.quote(textValue)));
     }
 
@@ -342,8 +329,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector textContains(@NonNull String substring) {
-        checkNotNull(substring, "substring cannot be null");
-
+        requireNonNull(substring, "substring cannot be null");
         return text(RegexHelper.getPatternContains(substring));
     }
 
@@ -356,8 +342,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector textStartsWith(@NonNull String substring) {
-        checkNotNull(substring, "substring cannot be null");
-
+        requireNonNull(substring, "substring cannot be null");
         return text(RegexHelper.getPatternStartsWith(substring));
     }
 
@@ -370,8 +355,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector textEndsWith(@NonNull String substring) {
-        checkNotNull(substring, "substring cannot be null");
-
+        requireNonNull(substring, "substring cannot be null");
         return text(RegexHelper.getPatternEndsWith(substring));
     }
 
@@ -383,8 +367,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector text(@NonNull Pattern textValue) {
-        checkNotNull(textValue, "textValue cannot be null");
-
+        requireNonNull(textValue, "textValue cannot be null");
         if (mText != null) {
             throw new IllegalStateException("Text selector is already defined");
         }
@@ -581,7 +564,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector hasParent(@NonNull BySelector parentSelector) {
-        checkNotNull(parentSelector, "parentSelector cannot be null");
+        requireNonNull(parentSelector, "parentSelector cannot be null");
         return hasAncestor(parentSelector, 1);
     }
 
@@ -594,7 +577,7 @@
      * @return A reference to this {@link BySelector}.
      */
     public @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector) {
-        checkNotNull(ancestorSelector, "ancestorSelector cannot be null");
+        requireNonNull(ancestorSelector, "ancestorSelector cannot be null");
         if (mAncestorSelector != null) {
             throw new IllegalStateException("Parent/ancestor selector is already defined");
         }
@@ -631,7 +614,7 @@
      * @throws IllegalArgumentException if the selector has a parent/ancestor selector
      */
     public @NonNull BySelector hasChild(@NonNull BySelector childSelector) {
-        checkNotNull(childSelector, "childSelector cannot be null");
+        requireNonNull(childSelector, "childSelector cannot be null");
         return hasDescendant(childSelector, 1);
     }
 
@@ -646,7 +629,7 @@
      * @throws IllegalArgumentException if the selector has a parent/ancestor selector
      */
     public @NonNull BySelector hasDescendant(@NonNull BySelector descendantSelector) {
-        checkNotNull(descendantSelector, "descendantSelector cannot be null");
+        requireNonNull(descendantSelector, "descendantSelector cannot be null");
         if (descendantSelector.mAncestorSelector != null) {
             // Search root is ambiguous with nested parent selectors.
             throw new IllegalArgumentException(
@@ -735,11 +718,4 @@
         builder.append("]");
         return builder.toString();
     }
-
-    private static <T> T checkNotNull(T value, @NonNull String message) {
-        if (value == null) {
-            throw new NullPointerException(message);
-        }
-        return value;
-    }
 }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
index 5cb978a..0eda6d6 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
@@ -16,6 +16,8 @@
 
 package androidx.test.uiautomator;
 
+import static java.util.Objects.requireNonNull;
+
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -109,7 +111,7 @@
      */
     @NonNull
     public UiSelector text(@NonNull String text) {
-        checkNotNull(text, "text cannot be null");
+        requireNonNull(text, "text cannot be null");
         return buildSelector(SELECTOR_TEXT, text);
     }
 
@@ -125,7 +127,7 @@
      */
     @NonNull
     public UiSelector textMatches(@NonNull String regex) {
-        checkNotNull(regex, "regex cannot be null");
+        requireNonNull(regex, "regex cannot be null");
         return buildSelector(SELECTOR_TEXT_REGEX, Pattern.compile(regex, Pattern.DOTALL));
     }
 
@@ -140,7 +142,7 @@
      */
     @NonNull
     public UiSelector textStartsWith(@NonNull String text) {
-        checkNotNull(text, "text cannot be null");
+        requireNonNull(text, "text cannot be null");
         return buildSelector(SELECTOR_START_TEXT, text);
     }
 
@@ -155,7 +157,7 @@
      */
     @NonNull
     public UiSelector textContains(@NonNull String text) {
-        checkNotNull(text, "text cannot be null");
+        requireNonNull(text, "text cannot be null");
         return buildSelector(SELECTOR_CONTAINS_TEXT, text);
     }
 
@@ -168,7 +170,7 @@
      */
     @NonNull
     public UiSelector className(@NonNull String className) {
-        checkNotNull(className, "className cannot be null");
+        requireNonNull(className, "className cannot be null");
         return buildSelector(SELECTOR_CLASS, className);
     }
 
@@ -181,7 +183,7 @@
      */
     @NonNull
     public UiSelector classNameMatches(@NonNull String regex) {
-        checkNotNull(regex, "regex cannot be null");
+        requireNonNull(regex, "regex cannot be null");
         return buildSelector(SELECTOR_CLASS_REGEX, Pattern.compile(regex));
     }
 
@@ -194,7 +196,7 @@
      */
     @NonNull
     public <T> UiSelector className(@NonNull Class<T> type) {
-        checkNotNull(type, "type cannot be null");
+        requireNonNull(type, "type cannot be null");
         return buildSelector(SELECTOR_CLASS, type.getName());
     }
 
@@ -216,7 +218,7 @@
      */
     @NonNull
     public UiSelector description(@NonNull String desc) {
-        checkNotNull(desc, "desc cannot be null");
+        requireNonNull(desc, "desc cannot be null");
         return buildSelector(SELECTOR_DESCRIPTION, desc);
     }
 
@@ -236,7 +238,7 @@
      */
     @NonNull
     public UiSelector descriptionMatches(@NonNull String regex) {
-        checkNotNull(regex, "regex cannot be null");
+        requireNonNull(regex, "regex cannot be null");
         return buildSelector(SELECTOR_DESCRIPTION_REGEX, Pattern.compile(regex, Pattern.DOTALL));
     }
 
@@ -258,7 +260,7 @@
      */
     @NonNull
     public UiSelector descriptionStartsWith(@NonNull String desc) {
-        checkNotNull(desc, "desc cannot be null");
+        requireNonNull(desc, "desc cannot be null");
         return buildSelector(SELECTOR_START_DESCRIPTION, desc);
     }
 
@@ -280,7 +282,7 @@
      */
     @NonNull
     public UiSelector descriptionContains(@NonNull String desc) {
-        checkNotNull(desc, "desc cannot be null");
+        requireNonNull(desc, "desc cannot be null");
         return buildSelector(SELECTOR_CONTAINS_DESCRIPTION, desc);
     }
 
@@ -292,7 +294,7 @@
      */
     @NonNull
     public UiSelector resourceId(@NonNull String id) {
-        checkNotNull(id, "id cannot be null");
+        requireNonNull(id, "id cannot be null");
         return buildSelector(SELECTOR_RESOURCE_ID, id);
     }
 
@@ -305,7 +307,7 @@
      */
     @NonNull
     public UiSelector resourceIdMatches(@NonNull String regex) {
-        checkNotNull(regex, "regex cannot be null");
+        requireNonNull(regex, "regex cannot be null");
         return buildSelector(SELECTOR_RESOURCE_ID_REGEX, Pattern.compile(regex));
     }
 
@@ -537,7 +539,7 @@
      */
     @NonNull
     public UiSelector childSelector(@NonNull UiSelector selector) {
-        checkNotNull(selector, "selector cannot be null");
+        requireNonNull(selector, "selector cannot be null");
         return buildSelector(SELECTOR_CHILD, selector);
     }
 
@@ -561,7 +563,7 @@
      */
     @NonNull
     public UiSelector fromParent(@NonNull UiSelector selector) {
-        checkNotNull(selector, "selector cannot be null");
+        requireNonNull(selector, "selector cannot be null");
         return buildSelector(SELECTOR_PARENT, selector);
     }
 
@@ -574,7 +576,7 @@
      */
     @NonNull
     public UiSelector packageName(@NonNull String name) {
-        checkNotNull(name, "name cannot be null");
+        requireNonNull(name, "name cannot be null");
         return buildSelector(SELECTOR_PACKAGE_NAME, name);
     }
 
@@ -587,7 +589,7 @@
      */
     @NonNull
     public UiSelector packageNameMatches(@NonNull String regex) {
-        checkNotNull(regex, "regex cannot be null");
+        requireNonNull(regex, "regex cannot be null");
         return buildSelector(SELECTOR_PACKAGE_NAME_REGEX, Pattern.compile(regex));
     }
 
@@ -1022,11 +1024,4 @@
         builder.append("]");
         return builder.toString();
     }
-
-    private static <T> T checkNotNull(T value, @NonNull String message) {
-        if (value == null) {
-            throw new NullPointerException(message);
-        }
-        return value;
-    }
 }
diff --git a/testutils/testutils-appcompat/src/main/AndroidManifest.xml b/testutils/testutils-appcompat/src/main/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/testutils/testutils-appcompat/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/testutils/testutils-espresso/src/main/AndroidManifest.xml b/testutils/testutils-espresso/src/main/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/testutils/testutils-espresso/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/testutils/testutils-fonts/src/androidMain/AndroidManifest.xml b/testutils/testutils-fonts/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index ee19b21..0000000
--- a/testutils/testutils-fonts/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/testutils/testutils-macrobenchmark/src/main/AndroidManifest.xml b/testutils/testutils-macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/testutils/testutils-macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/testutils/testutils-mockito/src/main/AndroidManifest.xml b/testutils/testutils-mockito/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/testutils/testutils-mockito/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/testutils/testutils-runtime/src/main/AndroidManifest.xml b/testutils/testutils-runtime/src/main/AndroidManifest.xml
deleted file mode 100644
index c7003d8..0000000
--- a/testutils/testutils-runtime/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/text/text/src/androidTest/AndroidManifest.xml b/text/text/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/text/text/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/text/text/src/main/AndroidManifest.xml b/text/text/src/main/AndroidManifest.xml
deleted file mode 100644
index 7535bfb..0000000
--- a/text/text/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
index 1e55554..08ea852 100644
--- a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
+++ b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
@@ -37,6 +37,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.io.ByteArrayOutputStream;
@@ -68,6 +69,7 @@
     }
 
     @Test
+    @Ignore("b/280041271")
     public void beginAndEndSection() throws IOException {
         startTrace();
         Trace.beginSection("beginAndEndSection");
@@ -79,6 +81,7 @@
     }
 
     @Test
+    @Ignore("b/280041271")
     public void beginAndEndTraceSectionLongLabel() throws IOException {
         StringBuilder builder = new StringBuilder();
         for (int i = 0; i < 20; i++) {
@@ -120,6 +123,7 @@
     }
 
     @Test
+    @Ignore("b/280041271")
     public void isEnabledDuringTrace() throws IOException {
         startTrace();
         boolean enabled = Trace.isEnabled();
diff --git a/transition/transition-ktx/src/main/AndroidManifest.xml b/transition/transition-ktx/src/main/AndroidManifest.xml
deleted file mode 100644
index 61b6811..0000000
--- a/transition/transition-ktx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/transition/transition/src/main/AndroidManifest.xml b/transition/transition/src/main/AndroidManifest.xml
deleted file mode 100644
index 6136621..0000000
--- a/transition/transition/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/tv/samples/src/main/java/androidx/tv/samples/ButtonSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/ButtonSamples.kt
new file mode 100644
index 0000000..0b3501b
--- /dev/null
+++ b/tv/samples/src/main/java/androidx/tv/samples/ButtonSamples.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalTvMaterial3Api::class)
+
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.tv.material3.Button
+import androidx.tv.material3.ButtonDefaults
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Icon
+import androidx.tv.material3.OutlinedButton
+import androidx.tv.material3.Text
+
+@Sampled
+@Composable
+fun ButtonSample() {
+    Button(onClick = { }) {
+        Text("Button")
+    }
+}
+
+@Sampled
+@Composable
+fun OutlinedButtonSample() {
+    OutlinedButton(onClick = {}) {
+        Text("Outlined Button")
+    }
+}
+
+@Sampled
+@Composable
+fun LikeButtonSample() {
+    Button(
+        onClick = { /* Do something! */ },
+        contentPadding = ButtonDefaults.ButtonWithIconContentPadding
+    ) {
+        Icon(
+            Icons.Filled.Favorite,
+            contentDescription = "Localized description",
+            modifier = Modifier.size(ButtonDefaults.IconSize)
+        )
+        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+        Text("Like")
+    }
+}
diff --git a/tv/samples/src/main/java/androidx/tv/samples/CardLayoutSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/CardLayoutSamples.kt
new file mode 100644
index 0000000..c4eed33
--- /dev/null
+++ b/tv/samples/src/main/java/androidx/tv/samples/CardLayoutSamples.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalTvMaterial3Api::class)
+
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.tv.material3.CardLayoutDefaults
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.StandardCardLayout
+import androidx.tv.material3.Text
+import androidx.tv.material3.WideCardLayout
+
+@Sampled
+@Composable
+fun StandardCardLayoutSample() {
+    StandardCardLayout(
+        modifier = Modifier.size(150.dp, 120.dp),
+        imageCard = { interactionSource ->
+            CardLayoutDefaults.ImageCard(
+                onClick = { },
+                interactionSource = interactionSource
+            ) {
+                Box(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .height(80.dp)
+                        .background(Color.Blue)
+                )
+            }
+        },
+        title = { Text("Standard Card") }
+    )
+}
+
+@Sampled
+@Composable
+fun WideCardLayoutSample() {
+    WideCardLayout(
+        modifier = Modifier.size(180.dp, 100.dp),
+        imageCard = { interactionSource ->
+            CardLayoutDefaults.ImageCard(
+                onClick = { },
+                interactionSource = interactionSource
+            ) {
+                Box(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .height(90.dp)
+                        .background(Color.Blue)
+                )
+            }
+        },
+        title = { Text("Wide Card", Modifier.padding(start = 8.dp)) },
+    )
+}
diff --git a/tv/samples/src/main/java/androidx/tv/samples/CardSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/CardSamples.kt
new file mode 100644
index 0000000..ae8440b
--- /dev/null
+++ b/tv/samples/src/main/java/androidx/tv/samples/CardSamples.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalTvMaterial3Api::class)
+
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.tv.material3.Card
+import androidx.tv.material3.ClassicCard
+import androidx.tv.material3.CompactCard
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Text
+import androidx.tv.material3.WideClassicCard
+
+@Sampled
+@Composable
+fun CardSample() {
+    Card(
+        modifier = Modifier.size(150.dp, 120.dp),
+        onClick = { }
+    ) {
+        Box(Modifier.fillMaxSize()) {
+            Text(
+                text = "Card",
+                modifier = Modifier.align(Alignment.Center)
+            )
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun ClassicCardSample() {
+    ClassicCard(
+        modifier = Modifier.size(150.dp, 120.dp),
+        image = {
+            Box(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(80.dp)
+                    .background(Color.Blue)
+            )
+        },
+        title = {
+            Text("Classic Card")
+        },
+        contentPadding = PaddingValues(8.dp),
+        onClick = { }
+    )
+}
+
+@Sampled
+@Composable
+fun CompactCardSample() {
+    CompactCard(
+        modifier = Modifier.size(150.dp, 120.dp),
+        image = {
+            Box(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(80.dp)
+                    .background(Color.Blue)
+            )
+        },
+        title = {
+            Text(
+                text = "Compact Card",
+                modifier = Modifier.padding(8.dp)
+            )
+        },
+        onClick = { }
+    )
+}
+
+@Sampled
+@Composable
+fun WideClassicCardSample() {
+    WideClassicCard(
+        modifier = Modifier.size(180.dp, 100.dp),
+        image = {
+            Box(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(80.dp)
+                    .background(Color.Blue)
+            )
+        },
+        title = {
+            Text(
+                text = "Wide Classic Card",
+                modifier = Modifier.padding(start = 8.dp)
+            )
+        },
+        contentPadding = PaddingValues(8.dp),
+        onClick = { }
+    )
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java b/tv/samples/src/main/java/androidx/tv/samples/CheckboxSamples.kt
similarity index 63%
copy from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
copy to tv/samples/src/main/java/androidx/tv/samples/CheckboxSamples.kt
index c325c57..f6af570 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
+++ b/tv/samples/src/main/java/androidx/tv/samples/CheckboxSamples.kt
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-package androidx.appactions.interaction.capabilities.core.testing.spec;
+@file:OptIn(ExperimentalTvMaterial3Api::class)
 
-/** Sample enum value for testing. */
-public enum TestEnum {
-    VALUE_1,
-    VALUE_2,
-}
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.runtime.Composable
+import androidx.tv.material3.Checkbox
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+
+@Sampled
+@Composable
+fun CheckboxSample() {
+    Checkbox(checked = true, onCheckedChange = { })
+}
\ No newline at end of file
diff --git a/tv/samples/src/main/java/androidx/tv/samples/IconButtonSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/IconButtonSamples.kt
new file mode 100644
index 0000000..8694f8f
--- /dev/null
+++ b/tv/samples/src/main/java/androidx/tv/samples/IconButtonSamples.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalTvMaterial3Api::class)
+
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.outlined.FavoriteBorder
+import androidx.compose.runtime.Composable
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Icon
+import androidx.tv.material3.IconButton
+import androidx.tv.material3.OutlinedIconButton
+
+@Sampled
+@Composable
+fun IconButtonSample() {
+    IconButton(onClick = { /* doSomething() */ }) {
+        Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
+    }
+}
+
+@Sampled
+@Composable
+fun OutlinedIconButtonSample() {
+    OutlinedIconButton(onClick = { /* doSomething() */ }) {
+        Icon(
+            Icons.Outlined.FavoriteBorder,
+            contentDescription = "Localized description"
+        )
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java b/tv/samples/src/main/java/androidx/tv/samples/RadioButtonSamples.kt
similarity index 63%
copy from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
copy to tv/samples/src/main/java/androidx/tv/samples/RadioButtonSamples.kt
index c325c57..e9896b5 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
+++ b/tv/samples/src/main/java/androidx/tv/samples/RadioButtonSamples.kt
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-package androidx.appactions.interaction.capabilities.core.testing.spec;
+@file:OptIn(ExperimentalTvMaterial3Api::class)
 
-/** Sample enum value for testing. */
-public enum TestEnum {
-    VALUE_1,
-    VALUE_2,
-}
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.runtime.Composable
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.RadioButton
+
+@Sampled
+@Composable
+fun RadioButtonSample() {
+    RadioButton(selected = true, onClick = {})
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java b/tv/samples/src/main/java/androidx/tv/samples/SwitchSamples.kt
similarity index 63%
copy from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
copy to tv/samples/src/main/java/androidx/tv/samples/SwitchSamples.kt
index c325c57..2e6b5da 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEnum.java
+++ b/tv/samples/src/main/java/androidx/tv/samples/SwitchSamples.kt
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-package androidx.appactions.interaction.capabilities.core.testing.spec;
+@file:OptIn(ExperimentalTvMaterial3Api::class)
 
-/** Sample enum value for testing. */
-public enum TestEnum {
-    VALUE_1,
-    VALUE_2,
-}
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.runtime.Composable
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Switch
+
+@Sampled
+@Composable
+fun SwitchSample() {
+    Switch(checked = true, onCheckedChange = { })
+}
\ No newline at end of file
diff --git a/tv/samples/src/main/java/androidx/tv/samples/WideButtonSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/WideButtonSamples.kt
new file mode 100644
index 0000000..46e2f94
--- /dev/null
+++ b/tv/samples/src/main/java/androidx/tv/samples/WideButtonSamples.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalTvMaterial3Api::class)
+
+package androidx.tv.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.runtime.Composable
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Icon
+import androidx.tv.material3.Text
+import androidx.tv.material3.WideButton
+
+@Sampled
+@Composable
+fun WideButtonSample() {
+    WideButton(onClick = { }) {
+        Text("Settings")
+    }
+}
+
+@Sampled
+@Composable
+fun WideButtonWithIcon() {
+    WideButton(
+        onClick = { },
+        title = { Text("Settings") },
+        icon = {
+            Icon(
+                imageVector = Icons.Default.Settings,
+                contentDescription = "Settings"
+            )
+        }
+    )
+}
+
+@Sampled
+@Composable
+fun WideButtonWithSubtitle() {
+    WideButton(
+        onClick = { },
+        title = { Text("Settings") },
+        subtitle = { Text(text = "Update device preferences") },
+    )
+}
+
+@Sampled
+@Composable
+fun WideButtonWithIconAndSubtitle() {
+    WideButton(
+        onClick = { },
+        title = { Text("Settings") },
+        subtitle = { Text(text = "Update device preferences") },
+        icon = {
+            Icon(
+                imageVector = Icons.Default.Settings,
+                contentDescription = "Settings"
+            )
+        }
+    )
+}
\ No newline at end of file
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridItemProvider.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridItemProvider.kt
index b9589a3..c310814 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridItemProvider.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridItemProvider.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
-import androidx.compose.foundation.lazy.layout.PinnableItem
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnableItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
@@ -66,10 +66,10 @@
     override fun getContentType(index: Int): Any? = gridContent.getContentType(index)
 
     @Composable
-    override fun Item(index: Int) {
-        gridContent.PinnableItem(index, state.pinnedItems) { localIndex ->
-            with(TvLazyGridItemScopeImpl) {
-                item(localIndex)
+    override fun Item(index: Int, key: Any) {
+        LazyLayoutPinnableItem(key, index, state.pinnedItems) {
+            gridContent.withInterval(index) { localIndex, content ->
+                content.item(TvLazyGridItemScopeImpl, localIndex)
             }
         }
     }
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridScrollPosition.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridScrollPosition.kt
index b787f26..f713db2 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridScrollPosition.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGridScrollPosition.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
@@ -35,7 +36,7 @@
     var index by mutableStateOf(ItemIndex(initialIndex))
         private set
 
-    var scrollOffset by mutableStateOf(initialScrollOffset)
+    var scrollOffset by mutableIntStateOf(initialScrollOffset)
         private set
 
     private var hadFirstNotEmptyLayout = false
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/TvLazyGridState.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/TvLazyGridState.kt
index 3d9873f..037afba 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/TvLazyGridState.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/TvLazyGridState.kt
@@ -29,6 +29,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.listSaver
@@ -135,7 +136,7 @@
     /**
      * Needed for [animateScrollToItem]. Updated on every measure.
      */
-    internal var slotsPerLine: Int by mutableStateOf(0)
+    internal var slotsPerLine: Int by mutableIntStateOf(0)
 
     /**
      * Needed for [animateScrollToItem]. Updated on every measure.
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListItemProvider.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListItemProvider.kt
index 2780fac..d50cd62 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListItemProvider.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListItemProvider.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
-import androidx.compose.foundation.lazy.layout.PinnableItem
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnableItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
@@ -67,9 +67,11 @@
     override val itemCount: Int get() = listContent.itemCount
 
     @Composable
-    override fun Item(index: Int) {
-        listContent.PinnableItem(index, state.pinnedItems) { localIndex ->
-            with(itemScope) { item(localIndex) }
+    override fun Item(index: Int, key: Any) {
+        LazyLayoutPinnableItem(key, index, state.pinnedItems) {
+            listContent.withInterval(index) { localIndex, content ->
+                content.item(itemScope, localIndex)
+            }
         }
     }
 
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListScrollPosition.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListScrollPosition.kt
index 20eb9e9..f1c7352 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListScrollPosition.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyListScrollPosition.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
@@ -34,7 +35,7 @@
 ) {
     var index by mutableStateOf(DataIndex(initialIndex))
 
-    var scrollOffset by mutableStateOf(initialScrollOffset)
+    var scrollOffset by mutableIntStateOf(initialScrollOffset)
         private set
 
     private var hadFirstNotEmptyLayout = false
diff --git a/tv/tv-material/api/public_plus_experimental_current.txt b/tv/tv-material/api/public_plus_experimental_current.txt
index 093044e..16c8b91 100644
--- a/tv/tv-material/api/public_plus_experimental_current.txt
+++ b/tv/tv-material/api/public_plus_experimental_current.txt
@@ -141,13 +141,12 @@
   @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceBorder {
   }
 
-  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceColor {
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceColors {
   }
 
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceDefaults {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ClickableSurfaceBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedDisabledBorder);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ClickableSurfaceColor color(optional long color, optional long focusedColor, optional long pressedColor, optional long disabledColor);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ClickableSurfaceColor contentColor(optional long color, optional long focusedColor, optional long pressedColor, optional long disabledColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ClickableSurfaceColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method public androidx.tv.material3.ClickableSurfaceGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow);
     method public androidx.tv.material3.ClickableSurfaceScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale, optional @FloatRange(from=0.0) float disabledScale, optional @FloatRange(from=0.0) float focusedDisabledScale);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ClickableSurfaceShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape);
@@ -356,12 +355,12 @@
     method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.material3.DrawerState rememberDrawerState(androidx.tv.material3.DrawerValue initialValue);
   }
 
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class NonInteractiveSurfaceColors {
+  }
+
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class NonInteractiveSurfaceDefaults {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public long getColor();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public long getContentColor();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.NonInteractiveSurfaceColors colors(optional long containerColor, optional long contentColor);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getShape();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long color;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long contentColor;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape shape;
     field public static final androidx.tv.material3.NonInteractiveSurfaceDefaults INSTANCE;
   }
@@ -450,9 +449,9 @@
   }
 
   public final class SurfaceKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(optional androidx.compose.ui.Modifier modifier, optional float tonalElevation, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.tv.material3.Border border, optional androidx.tv.material3.Glow glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional float tonalElevation, optional androidx.tv.material3.ClickableSurfaceShape shape, optional androidx.tv.material3.ClickableSurfaceColor color, optional androidx.tv.material3.ClickableSurfaceColor contentColor, optional androidx.tv.material3.ClickableSurfaceScale scale, optional androidx.tv.material3.ClickableSurfaceBorder border, optional androidx.tv.material3.ClickableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional float tonalElevation, optional androidx.tv.material3.ToggleableSurfaceShape shape, optional androidx.tv.material3.ToggleableSurfaceColor color, optional androidx.tv.material3.ToggleableSurfaceColor contentColor, optional androidx.tv.material3.ToggleableSurfaceScale scale, optional androidx.tv.material3.ToggleableSurfaceBorder border, optional androidx.tv.material3.ToggleableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(optional androidx.compose.ui.Modifier modifier, optional float tonalElevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.tv.material3.NonInteractiveSurfaceColors colors, optional androidx.tv.material3.Border border, optional androidx.tv.material3.Glow glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional float tonalElevation, optional androidx.tv.material3.ClickableSurfaceShape shape, optional androidx.tv.material3.ClickableSurfaceColors colors, optional androidx.tv.material3.ClickableSurfaceScale scale, optional androidx.tv.material3.ClickableSurfaceBorder border, optional androidx.tv.material3.ClickableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional float tonalElevation, optional androidx.tv.material3.ToggleableSurfaceShape shape, optional androidx.tv.material3.ToggleableSurfaceColors colors, optional androidx.tv.material3.ToggleableSurfaceScale scale, optional androidx.tv.material3.ToggleableSurfaceBorder border, optional androidx.tv.material3.ToggleableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
@@ -509,13 +508,12 @@
   @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceBorder {
   }
 
-  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceColor {
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceColors {
   }
 
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceDefaults {
     method public androidx.tv.material3.ToggleableSurfaceBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border selectedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedSelectedBorder, optional androidx.tv.material3.Border focusedDisabledBorder, optional androidx.tv.material3.Border pressedSelectedBorder, optional androidx.tv.material3.Border selectedDisabledBorder, optional androidx.tv.material3.Border focusedSelectedDisabledBorder);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ToggleableSurfaceColor color(optional long color, optional long focusedColor, optional long pressedColor, optional long selectedColor, optional long disabledColor, optional long focusedSelectedColor, optional long pressedSelectedColor);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ToggleableSurfaceColor contentColor(optional long color, optional long focusedColor, optional long pressedColor, optional long selectedColor, optional long disabledColor, optional long focusedSelectedColor, optional long pressedSelectedColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ToggleableSurfaceColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor, optional long selectedContainerColor, optional long selectedContentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long focusedSelectedContainerColor, optional long focusedSelectedContentColor, optional long pressedSelectedContainerColor, optional long pressedSelectedContentColor);
     method public androidx.tv.material3.ToggleableSurfaceGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow, optional androidx.tv.material3.Glow selectedGlow, optional androidx.tv.material3.Glow focusedSelectedGlow, optional androidx.tv.material3.Glow pressedSelectedGlow);
     method public androidx.tv.material3.ToggleableSurfaceScale scale(optional float scale, optional float focusedScale, optional float pressedScale, optional float selectedScale, optional float disabledScale, optional float focusedSelectedScale, optional float focusedDisabledScale, optional float pressedSelectedScale, optional float selectedDisabledScale, optional float focusedSelectedDisabledScale);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ToggleableSurfaceShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape selectedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedSelectedShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape, optional androidx.compose.ui.graphics.Shape pressedSelectedShape, optional androidx.compose.ui.graphics.Shape selectedDisabledShape, optional androidx.compose.ui.graphics.Shape focusedSelectedDisabledShape);
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/NonInteractiveSurfaceScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/NonInteractiveSurfaceScreenshotTest.kt
index c23e30b..f317957 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/NonInteractiveSurfaceScreenshotTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/NonInteractiveSurfaceScreenshotTest.kt
@@ -96,7 +96,10 @@
     fun nonInteractiveSurface_containerColor() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(containerModifier.testTag(wrapperTestTag)) {
-                Surface(surfaceModifier(), color = Color.Green) {}
+                Surface(
+                    surfaceModifier(),
+                    colors = NonInteractiveSurfaceDefaults.colors(containerColor = Color.Green)
+                ) {}
             }
         }
         assertAgainstGolden("non_interactive_surface_${scheme.name}_containerColor")
@@ -106,7 +109,10 @@
     fun nonInteractiveSurface_contentColor() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(containerModifier.testTag(wrapperTestTag)) {
-                Surface(surfaceModifier(), contentColor = Color.Red) {}
+                Surface(
+                    surfaceModifier(),
+                    colors = NonInteractiveSurfaceDefaults.colors(contentColor = Color.Red)
+                ) {}
             }
         }
         assertAgainstGolden("non_interactive_surface_${scheme.name}_contentColor")
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt
index 9f25017..9684230 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt
@@ -112,8 +112,8 @@
                     shape = ClickableSurfaceDefaults.shape(
                         shape = RectangleShape
                     ),
-                    color = ClickableSurfaceDefaults.color(
-                        color = Color.Yellow
+                    colors = ClickableSurfaceDefaults.colors(
+                        containerColor = Color.Yellow
                     )
                 ) {
                     Box(Modifier.fillMaxSize())
@@ -123,8 +123,8 @@
                     shape = ClickableSurfaceDefaults.shape(
                         shape = RectangleShape
                     ),
-                    color = ClickableSurfaceDefaults.color(
-                        color = Color.Green
+                    colors = ClickableSurfaceDefaults.colors(
+                        containerColor = Color.Green
                     )
                 ) {
                     Box(Modifier.fillMaxSize())
@@ -180,7 +180,7 @@
                     },
                     onClick = {},
                     tonalElevation = 2.toDp(),
-                    contentColor = ClickableSurfaceDefaults.color(color = expectedColor)
+                    colors = ClickableSurfaceDefaults.colors(contentColor = expectedColor)
                 ) {}
             }
         }
@@ -379,9 +379,9 @@
                     .testTag("surface")
                     .size(100.toDp()),
                 onClick = {},
-                color = ClickableSurfaceDefaults.color(
-                    color = Color.Transparent,
-                    focusedColor = Color.Transparent
+                colors = ClickableSurfaceDefaults.colors(
+                    containerColor = Color.Transparent,
+                    focusedContainerColor = Color.Transparent
                 ),
                 glow = ClickableSurfaceDefaults.glow(
                     glow = Glow(
@@ -447,9 +447,9 @@
                         border = BorderStroke(width = 5.toDp(), color = Color.Magenta)
                     )
                 ),
-                color = ClickableSurfaceDefaults.color(
-                    color = Color.Transparent,
-                    focusedColor = Color.Transparent
+                colors = ClickableSurfaceDefaults.colors(
+                    containerColor = Color.Transparent,
+                    focusedContainerColor = Color.Transparent
                 )
             ) {}
         }
@@ -661,9 +661,9 @@
                     .testTag("surface")
                     .size(100.toDp()),
                 onCheckedChange = { isChecked = it },
-                color = ToggleableSurfaceDefaults.color(
-                    color = Color.Transparent,
-                    selectedColor = Color.Transparent
+                colors = ToggleableSurfaceDefaults.colors(
+                    containerColor = Color.Transparent,
+                    selectedContainerColor = Color.Transparent
                 ),
                 glow = ToggleableSurfaceDefaults.glow(
                     glow = Glow(
@@ -746,9 +746,9 @@
                         border = BorderStroke(width = 5.toDp(), color = Color.Magenta)
                     )
                 ),
-                color = ToggleableSurfaceDefaults.color(
-                    color = Color.Transparent,
-                    selectedColor = Color.Transparent
+                colors = ToggleableSurfaceDefaults.colors(
+                    containerColor = Color.Transparent,
+                    selectedContainerColor = Color.Transparent
                 )
             ) {}
         }
@@ -779,9 +779,9 @@
                     .testTag("surface"),
                 onClick = {},
                 enabled = surfaceEnabled,
-                color = ClickableSurfaceDefaults.color(
-                    color = Color.Green,
-                    disabledColor = Color.Red
+                colors = ClickableSurfaceDefaults.colors(
+                    containerColor = Color.Green,
+                    disabledContainerColor = Color.Red
                 )
             ) {}
         }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt
index b547d9d..6d4dee9 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt
@@ -48,6 +48,10 @@
  *
  * The default text style for internal [Text] components will be set to [Typography.labelLarge].
  *
+ * Samples:
+ * @sample androidx.tv.samples.ButtonSample
+ * @sample androidx.tv.samples.LikeButtonSample
+ *
  * @param onClick called when this button is clicked
  * @param modifier the [Modifier] to be applied to this button
  * @param enabled controls the enabled state of this button. When `false`, this component will not
@@ -115,6 +119,9 @@
  *
  * The default text style for internal [Text] components will be set to [Typography.labelLarge].
  *
+ * Samples:
+ * @sample androidx.tv.samples.OutlinedButtonSample
+ *
  * @param onClick called when this button is clicked
  * @param modifier the [Modifier] to be applied to this button
  * @param enabled controls the enabled state of this button. When `false`, this component will not
@@ -190,8 +197,7 @@
         scale = scale.toClickableSurfaceScale(),
         glow = glow.toClickableSurfaceGlow(),
         shape = shape.toClickableSurfaceShape(),
-        color = colors.toClickableSurfaceContainerColor(),
-        contentColor = colors.toClickableSurfaceContentColor(),
+        colors = colors.toClickableSurfaceColors(),
         tonalElevation = tonalElevation,
         border = border.toClickableSurfaceBorder(),
         interactionSource = interactionSource
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
index 560ad85..3661d23 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
@@ -285,6 +285,8 @@
     }
 }
 
+private val WideButtonContainerColor = Color.Transparent
+
 @OptIn(ExperimentalTvMaterial3Api::class)
 internal fun ButtonShape.toClickableSurfaceShape(): ClickableSurfaceShape = ClickableSurfaceShape(
     shape = shape,
@@ -295,30 +297,29 @@
 )
 
 @OptIn(ExperimentalTvMaterial3Api::class)
-internal fun ButtonColors.toClickableSurfaceContainerColor(): ClickableSurfaceColor =
-    ClickableSurfaceColor(
-        color = containerColor,
-        focusedColor = focusedContainerColor,
-        pressedColor = pressedContainerColor,
-        disabledColor = disabledContainerColor,
+internal fun ButtonColors.toClickableSurfaceColors(): ClickableSurfaceColors =
+    ClickableSurfaceColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledContentColor = disabledContentColor
     )
 
 @OptIn(ExperimentalTvMaterial3Api::class)
-internal fun ButtonColors.toClickableSurfaceContentColor(): ClickableSurfaceColor =
-    ClickableSurfaceColor(
-        color = contentColor,
-        focusedColor = focusedContentColor,
-        pressedColor = pressedContentColor,
-        disabledColor = disabledContentColor,
-    )
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-internal fun WideButtonContentColor.toClickableSurfaceContentColor(): ClickableSurfaceColor =
-    ClickableSurfaceColor(
-        color = contentColor,
-        focusedColor = focusedContentColor,
-        pressedColor = pressedContentColor,
-        disabledColor = disabledContentColor,
+internal fun WideButtonContentColor.toClickableSurfaceColors(): ClickableSurfaceColors =
+    ClickableSurfaceColors(
+        containerColor = WideButtonContainerColor,
+        contentColor = contentColor,
+        focusedContainerColor = WideButtonContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = WideButtonContainerColor,
+        pressedContentColor = pressedContentColor,
+        disabledContainerColor = WideButtonContainerColor,
+        disabledContentColor = disabledContentColor
     )
 
 @OptIn(ExperimentalTvMaterial3Api::class)
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
index 1c1fc72..38550b6 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
@@ -46,6 +46,8 @@
  *
  * This Card handles click events, calling its [onClick] lambda.
  *
+ * @sample androidx.tv.samples.CardSample
+ *
  * @param onClick called when this card is clicked
  * @param modifier the [Modifier] to be applied to this card
  * @param shape [CardShape] defines the shape of this card's container in different interaction
@@ -80,8 +82,7 @@
         onClick = onClick,
         modifier = modifier,
         shape = shape.toClickableSurfaceShape(),
-        color = colors.toClickableSurfaceContainerColor(),
-        contentColor = colors.toClickableSurfaceContentColor(),
+        colors = colors.toClickableSurfaceColors(),
         scale = scale.toClickableSurfaceScale(),
         border = border.toClickableSurfaceBorder(),
         glow = glow.toClickableSurfaceGlow(),
@@ -100,6 +101,8 @@
  *
  * This Card handles click events, calling its [onClick] lambda.
  *
+ * @sample androidx.tv.samples.ClassicCardSample
+ *
  * @param onClick called when this card is clicked
  * @param image defines the [Composable] image to be displayed on top of the Card.
  * @param title defines the [Composable] title placed below the image in the Card.
@@ -176,6 +179,8 @@
  *
  * This Card handles click events, calling its [onClick] lambda.
  *
+ * @sample androidx.tv.samples.CompactCardSample
+ *
  * @param onClick called when this card is clicked
  * @param image defines the [Composable] image to be displayed on top of the Card.
  * @param title defines the [Composable] title placed below the image in the Card.
@@ -257,6 +262,8 @@
  *
  * This Card handles click events, calling its [onClick] lambda.
  *
+ * @sample androidx.tv.samples.WideClassicCardSample
+ *
  * @param onClick called when this card is clicked
  * @param image defines the [Composable] image to be displayed on top of the Card.
  * @param title defines the [Composable] title placed below the image in the Card.
@@ -526,21 +533,16 @@
 private const val DescriptionAlpha = 0.8f
 
 @OptIn(ExperimentalTvMaterial3Api::class)
-private fun CardColors.toClickableSurfaceContainerColor() =
-    ClickableSurfaceColor(
-        color = containerColor,
-        focusedColor = focusedContainerColor,
-        pressedColor = pressedContainerColor,
-        disabledColor = containerColor
-    )
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-private fun CardColors.toClickableSurfaceContentColor() =
-    ClickableSurfaceColor(
-        color = contentColor,
-        focusedColor = focusedContentColor,
-        pressedColor = pressedContentColor,
-        disabledColor = contentColor
+private fun CardColors.toClickableSurfaceColors() =
+    ClickableSurfaceColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor,
+        disabledContainerColor = containerColor,
+        disabledContentColor = contentColor
     )
 
 @OptIn(ExperimentalTvMaterial3Api::class)
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt
index 92aa581..df431a3 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt
@@ -40,6 +40,8 @@
  * It provides a vertical layout with an image card slot at the top. And below that, there are
  * slots for the title, subtitle and description.
  *
+ * @sample androidx.tv.samples.StandardCardLayoutSample
+ *
  * @param imageCard defines the [Composable] to be used for the image card. See
  * [CardLayoutDefaults.ImageCard] to create an image card. The `interactionSource` param provided
  * in the lambda function should be forwarded and used with the image card composable.
@@ -101,6 +103,8 @@
  * It provides a horizontal layout with an image card slot at the start, followed by the title,
  * subtitle and description at the end.
  *
+ * @sample androidx.tv.samples.WideCardLayoutSample
+ *
  * @param imageCard defines the [Composable] to be used for the image card. See
  * [CardLayoutDefaults.ImageCard] to create an image card. The `interactionSource` param provided
  * in the lambda function should to be forwarded and used with the image card composable.
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index 001aed4..5fbfc80c 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -38,6 +38,7 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -339,12 +340,12 @@
 @Stable
 @ExperimentalTvMaterial3Api
 class CarouselState(initialActiveItemIndex: Int = 0) {
-    internal var activePauseHandlesCount by mutableStateOf(0)
+    internal var activePauseHandlesCount by mutableIntStateOf(0)
 
     /**
      * The index of the item that is currently displayed by the carousel
      */
-    var activeItemIndex by mutableStateOf(initialActiveItemIndex)
+    var activeItemIndex by mutableIntStateOf(initialActiveItemIndex)
         internal set
 
     /**
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Checkbox.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Checkbox.kt
index 960df60..d6900dd 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Checkbox.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Checkbox.kt
@@ -64,6 +64,8 @@
  *
  * @see [TriStateCheckbox] if you require support for an indeterminate state.
  *
+ * @sample androidx.tv.samples.CheckboxSample
+ *
  * @param checked whether this checkbox is checked or unchecked
  * @param onCheckedChange called when this checkbox is clicked. If `null`, then this checkbox will
  * not be interactable, unless something else handles its input events and updates its state.
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/IconButton.kt b/tv/tv-material/src/main/java/androidx/tv/material3/IconButton.kt
index 1c66a17..408ab4f 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/IconButton.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/IconButton.kt
@@ -42,6 +42,8 @@
  *
  * The default text style for internal [Text] components will be set to [Typography.labelLarge].
  *
+ * @sample androidx.tv.samples.IconButtonSample
+ *
  * @param onClick called when this button is clicked
  * @param modifier the [Modifier] to be applied to this button
  * @param enabled controls the enabled state of this button. When `false`, this component will not
@@ -79,8 +81,7 @@
         onClick = onClick,
         enabled = enabled,
         shape = shape.toClickableSurfaceShape(),
-        color = colors.toClickableSurfaceContainerColor(),
-        contentColor = colors.toClickableSurfaceContentColor(),
+        colors = colors.toClickableSurfaceColors(),
         scale = scale.toClickableSurfaceScale(),
         border = border.toClickableSurfaceBorder(),
         glow = glow.toClickableSurfaceGlow(),
@@ -107,6 +108,8 @@
  *
  * The default text style for internal [Text] components will be set to [Typography.labelLarge].
  *
+ * @sample androidx.tv.samples.OutlinedIconButtonSample
+ *
  * @param onClick called when this button is clicked
  * @param modifier the [Modifier] to be applied to this button
  * @param enabled controls the enabled state of this button. When `false`, this component will not
@@ -144,8 +147,7 @@
         onClick = onClick,
         enabled = enabled,
         shape = shape.toClickableSurfaceShape(),
-        color = colors.toClickableSurfaceContainerColor(),
-        contentColor = colors.toClickableSurfaceContentColor(),
+        colors = colors.toClickableSurfaceColors(),
         scale = scale.toClickableSurfaceScale(),
         border = border.toClickableSurfaceBorder(),
         glow = glow.toClickableSurfaceGlow(),
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ImmersiveList.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ImmersiveList.kt
index f55c49d..e10af5a 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ImmersiveList.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ImmersiveList.kt
@@ -30,6 +30,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -66,7 +67,7 @@
     listAlignment: Alignment = Alignment.BottomEnd,
     list: @Composable ImmersiveListScope.() -> Unit,
 ) {
-    var currentItemIndex by remember { mutableStateOf(0) }
+    var currentItemIndex by remember { mutableIntStateOf(0) }
     var listHasFocus by remember { mutableStateOf(false) }
 
     Box(modifier.bringIntoViewIfChildrenAreFocused()) {
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/RadioButton.kt b/tv/tv-material/src/main/java/androidx/tv/material3/RadioButton.kt
index 33e5a85..bf6c3fc 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/RadioButton.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/RadioButton.kt
@@ -47,6 +47,8 @@
  *
  * ![Radio button image](https://developer.android.com/images/reference/androidx/compose/material3/radio-button.png)
  *
+ * @sample androidx.tv.samples.RadioButtonSample
+ *
  * @param selected whether this radio button is selected or not
  * @param onClick called when this radio button is clicked. If `null`, then this radio button will
  * not be interactable, unless something else handles its input events and updates its state.
@@ -72,7 +74,8 @@
 ) {
     val dotRadius = animateDpAsState(
         targetValue = if (selected) RadioButtonDotSize / 2 else 0.dp,
-        animationSpec = tween(durationMillis = RadioAnimationDuration)
+        animationSpec = tween(durationMillis = RadioAnimationDuration),
+        label = "radioButton_dotRadius"
     )
     val radioColor = colors.radioColor(enabled, selected)
     val selectableModifier =
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
index 16f3dcc..2b563e7 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
@@ -65,8 +65,8 @@
  * @param tonalElevation When [color] is [ColorScheme.surface], a higher the elevation will result
  * in a darker color in light theme and lighter color in dark theme.
  * @param shape Defines the surface's shape.
- * @param color Color to be used on background of the Surface
- * @param contentColor The preferred content color provided by this Surface to its children.
+ * @param colors Defines the background & content color to be used in this Surface.
+ * See [NonInteractiveSurfaceDefaults.colors].
  * @param border Defines a border around the Surface.
  * @param glow Diffused shadow to be shown behind the Surface.
  * @param content defines the [Composable] content inside the surface
@@ -78,8 +78,7 @@
     modifier: Modifier = Modifier,
     tonalElevation: Dp = 0.dp,
     shape: Shape = NonInteractiveSurfaceDefaults.shape,
-    color: Color = NonInteractiveSurfaceDefaults.color,
-    contentColor: Color = NonInteractiveSurfaceDefaults.contentColor,
+    colors: NonInteractiveSurfaceColors = NonInteractiveSurfaceDefaults.colors(),
     border: Border = NonInteractiveSurfaceDefaults.border,
     glow: Glow = NonInteractiveSurfaceDefaults.glow,
     content: @Composable (BoxScope.() -> Unit)
@@ -90,8 +89,8 @@
         enabled = true,
         tonalElevation = tonalElevation,
         shape = shape,
-        color = color,
-        contentColor = contentColor,
+        color = colors.containerColor,
+        contentColor = colors.contentColor,
         scale = 1.0f,
         border = border,
         glow = glow,
@@ -114,8 +113,8 @@
  * @param tonalElevation When [color] is [ColorScheme.surface], a higher the elevation will result
  * in a darker color in light theme and lighter color in dark theme.
  * @param shape Defines the surface's shape.
- * @param color Color to be used on background of the Surface
- * @param contentColor The preferred content color provided by this Surface to its children.
+ * @param colors Defines the background & content colors to be used in this surface for different
+ * interaction states. See [ClickableSurfaceDefaults.colors].
  * @param scale Defines size of the Surface relative to its original size.
  * @param border Defines a border around the Surface.
  * @param glow Diffused shadow to be shown behind the Surface.
@@ -133,8 +132,7 @@
     enabled: Boolean = true,
     tonalElevation: Dp = 0.dp,
     shape: ClickableSurfaceShape = ClickableSurfaceDefaults.shape(),
-    color: ClickableSurfaceColor = ClickableSurfaceDefaults.color(),
-    contentColor: ClickableSurfaceColor = ClickableSurfaceDefaults.contentColor(),
+    colors: ClickableSurfaceColors = ClickableSurfaceDefaults.colors(),
     scale: ClickableSurfaceScale = ClickableSurfaceDefaults.scale(),
     border: ClickableSurfaceBorder = ClickableSurfaceDefaults.border(),
     glow: ClickableSurfaceGlow = ClickableSurfaceDefaults.glow(),
@@ -158,17 +156,17 @@
             pressed = pressed,
             shape = shape
         ),
-        color = ClickableSurfaceDefaults.color(
+        color = ClickableSurfaceDefaults.containerColor(
             enabled = enabled,
             focused = focused,
             pressed = pressed,
-            color = color
+            colors = colors
         ),
-        contentColor = ClickableSurfaceDefaults.color(
+        contentColor = ClickableSurfaceDefaults.contentColor(
             enabled = enabled,
             focused = focused,
             pressed = pressed,
-            color = contentColor
+            colors = colors
         ),
         scale = ClickableSurfaceDefaults.scale(
             enabled = enabled,
@@ -215,8 +213,8 @@
  * @param tonalElevation When [color] is [ColorScheme.surface], a higher the elevation will result
  * in a darker color in light theme and lighter color in dark theme.
  * @param shape Defines the surface's shape.
- * @param color Color to be used on background of the Surface
- * @param contentColor The preferred content color provided by this Surface to its children.
+ * @param colors  Defines the background & content colors to be used in this surface for different
+ * interaction states. See [ToggleableSurfaceDefaults.colors].
  * @param scale Defines size of the Surface relative to its original size.
  * @param border Defines a border around the Surface.
  * @param glow Diffused shadow to be shown behind the Surface.
@@ -235,8 +233,7 @@
     enabled: Boolean = true,
     tonalElevation: Dp = Elevation.Level0,
     shape: ToggleableSurfaceShape = ToggleableSurfaceDefaults.shape(),
-    color: ToggleableSurfaceColor = ToggleableSurfaceDefaults.color(),
-    contentColor: ToggleableSurfaceColor = ToggleableSurfaceDefaults.contentColor(),
+    colors: ToggleableSurfaceColors = ToggleableSurfaceDefaults.colors(),
     scale: ToggleableSurfaceScale = ToggleableSurfaceDefaults.scale(),
     border: ToggleableSurfaceBorder = ToggleableSurfaceDefaults.border(),
     glow: ToggleableSurfaceGlow = ToggleableSurfaceDefaults.glow(),
@@ -263,19 +260,19 @@
             selected = checked,
             shape = shape
         ),
-        color = ToggleableSurfaceDefaults.color(
+        color = ToggleableSurfaceDefaults.containerColor(
             enabled = enabled,
             focused = focused,
             pressed = pressed,
             selected = checked,
-            color = color
+            colors = colors
         ),
-        contentColor = ToggleableSurfaceDefaults.color(
+        contentColor = ToggleableSurfaceDefaults.contentColor(
             enabled = enabled,
             focused = focused,
             pressed = pressed,
             selected = checked,
-            color = contentColor
+            colors = colors
         ),
         scale = ToggleableSurfaceDefaults.scale(
             enabled = enabled,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceDefaults.kt b/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceDefaults.kt
index 8dc31ea..10cbe4c 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceDefaults.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceDefaults.kt
@@ -36,15 +36,21 @@
     val shape: Shape @ReadOnlyComposable @Composable get() = MaterialTheme.shapes.medium
 
     /**
-     * Represents the default container color used by a non-interactive [Surface]
+     * Creates a [NonInteractiveSurfaceColors] that represents the default container & content
+     * colors used by a non-interactive [Surface].
+     *
+     * @param containerColor the container color of this Surface
+     * @param contentColor the content color of this Surface
      */
-    val color: Color @ReadOnlyComposable @Composable get() = MaterialTheme.colorScheme.surface
-
-    /**
-     * Represents the default content color used by a non-interactive [Surface]
-     */
-    val contentColor: Color @ReadOnlyComposable @Composable get() =
-        MaterialTheme.colorScheme.onSurface
+    @ReadOnlyComposable
+    @Composable
+    fun colors(
+        containerColor: Color = MaterialTheme.colorScheme.surface,
+        contentColor: Color = contentColorFor(containerColor)
+    ) = NonInteractiveSurfaceColors(
+        containerColor = containerColor,
+        contentColor = contentColor
+    )
 
     /**
      * Represents the default border used by a non-interactive [Surface]
@@ -104,66 +110,68 @@
         focusedDisabledShape = focusedDisabledShape
     )
 
-    internal fun color(
+    internal fun containerColor(
         enabled: Boolean,
         focused: Boolean,
         pressed: Boolean,
-        color: ClickableSurfaceColor
+        colors: ClickableSurfaceColors
     ): Color {
         return when {
-            pressed && enabled -> color.pressedColor
-            focused && enabled -> color.focusedColor
-            enabled -> color.color
-            else -> color.disabledColor
+            pressed && enabled -> colors.pressedContainerColor
+            focused && enabled -> colors.focusedContainerColor
+            enabled -> colors.containerColor
+            else -> colors.disabledContainerColor
+        }
+    }
+
+    internal fun contentColor(
+        enabled: Boolean,
+        focused: Boolean,
+        pressed: Boolean,
+        colors: ClickableSurfaceColors
+    ): Color {
+        return when {
+            pressed && enabled -> colors.pressedContentColor
+            focused && enabled -> colors.focusedContentColor
+            enabled -> colors.contentColor
+            else -> colors.disabledContentColor
         }
     }
 
     /**
-     * Creates a [ClickableSurfaceColor] that represents the default container colors used in a
-     * Surface.
+     * Creates a [ClickableSurfaceColors] that represents the default container & content colors
+     * used in a Surface.
      *
-     * @param color the container color of this Surface when enabled
-     * @param focusedColor the container color of this Surface when enabled and focused
-     * @param pressedColor the container color of this Surface when enabled and pressed
-     * @param disabledColor the container color of this Surface when not enabled
+     * @param containerColor the container color of this Surface when enabled
+     * @param contentColor the content color of this Surface when enabled
+     * @param focusedContainerColor the container color of this Surface when enabled and focused
+     * @param focusedContentColor the content color of this Surface when enabled and focused
+     * @param pressedContainerColor the container color of this Surface when enabled and pressed
+     * @param pressedContentColor the content color of this Surface when enabled and pressed
+     * @param disabledContainerColor the container color of this Surface when not enabled
+     * @param disabledContentColor the content color of this Surface when not enabled
      */
     @ReadOnlyComposable
     @Composable
-    fun color(
-        color: Color = MaterialTheme.colorScheme.surface,
-        focusedColor: Color = MaterialTheme.colorScheme.inverseSurface,
-        pressedColor: Color = MaterialTheme.colorScheme.inverseSurface,
-        disabledColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(
-            alpha = DisabledBackgroundAlpha
-        )
-    ) = ClickableSurfaceColor(
-        color = color,
-        focusedColor = focusedColor,
-        pressedColor = pressedColor,
-        disabledColor = disabledColor
-    )
-
-    /**
-     * Creates a [ClickableSurfaceColor] that represents the default content colors used in a
-     * Surface.
-     *
-     * @param color the content color of this Surface when enabled
-     * @param focusedColor the content color of this Surface when enabled and focused
-     * @param pressedColor the content color of this Surface when enabled and pressed
-     * @param disabledColor the content color of this Surface when not enabled
-     */
-    @ReadOnlyComposable
-    @Composable
-    fun contentColor(
-        color: Color = MaterialTheme.colorScheme.onSurface,
-        focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
-        pressedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
-        disabledColor: Color = MaterialTheme.colorScheme.onSurface
-    ) = ClickableSurfaceColor(
-        color = color,
-        focusedColor = focusedColor,
-        pressedColor = pressedColor,
-        disabledColor = disabledColor
+    fun colors(
+        containerColor: Color = MaterialTheme.colorScheme.surface,
+        contentColor: Color = contentColorFor(containerColor),
+        focusedContainerColor: Color = MaterialTheme.colorScheme.inverseSurface,
+        focusedContentColor: Color = contentColorFor(focusedContainerColor),
+        pressedContainerColor: Color = focusedContainerColor,
+        pressedContentColor: Color = contentColorFor(pressedContainerColor),
+        disabledContainerColor: Color = MaterialTheme.colorScheme.surfaceVariant
+            .copy(alpha = DisabledContainerAlpha),
+        disabledContentColor: Color = MaterialTheme.colorScheme.onSurface
+    ) = ClickableSurfaceColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledContentColor = disabledContentColor
     )
 
     internal fun scale(
@@ -342,69 +350,71 @@
     )
 
     /**
-     * Creates a [ToggleableSurfaceColor] that represents the default container colors used in a
-     * toggleable Surface.
+     * Creates a [ToggleableSurfaceColors] that represents the default container & content colors
+     * used in a toggleable Surface.
      *
-     * @param color the color used when the Surface is enabled, and has no other [Interaction]s.
-     * @param focusedColor the color used when the Surface is enabled and focused.
-     * @param pressedColor the color used when the Surface is enabled and pressed.
-     * @param selectedColor the color used when the Surface is enabled and selected.
-     * @param disabledColor the color used when the Surface is not enabled.
-     * @param focusedSelectedColor the color used when the Surface is enabled, focused and selected.
-     * @param pressedSelectedColor the color used when the Surface is enabled, pressed and selected.
+     * @param containerColor the container color used when the Surface is enabled, and has no other
+     * [Interaction]s.
+     * @param contentColor the content color used when the Surface is enabled, and has no other
+     * [Interaction]s.
+     * @param focusedContainerColor the container color used when the Surface is enabled and
+     * focused.
+     * @param focusedContentColor the content color used when the Surface is enabled and
+     * focused.
+     * @param pressedContainerColor the container color used when the Surface is enabled and
+     * pressed.
+     * @param pressedContentColor the content color used when the Surface is enabled and
+     * pressed.
+     * @param selectedContainerColor the container color used when the Surface is enabled and
+     * selected.
+     * @param selectedContentColor the content color used when the Surface is enabled and
+     * selected.
+     * @param disabledContainerColor the container color used when the Surface is not enabled.
+     * @param disabledContentColor the content color used when the Surface is not enabled.
+     * @param focusedSelectedContainerColor the container color used when the Surface is enabled,
+     * focused and selected.
+     * @param focusedSelectedContentColor the content color used when the Surface is enabled,
+     * focused and selected.
+     * @param pressedSelectedContainerColor the container color used when the Surface is enabled,
+     * pressed and selected.
+     * @param pressedSelectedContentColor the content color used when the Surface is enabled,
+     * pressed and selected.
      */
     @ReadOnlyComposable
     @Composable
-    fun color(
-        color: Color = MaterialTheme.colorScheme.surface,
-        focusedColor: Color = MaterialTheme.colorScheme.inverseSurface,
-        pressedColor: Color = MaterialTheme.colorScheme.inverseSurface,
-        selectedColor: Color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f),
-        disabledColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(
-            alpha = DisabledBackgroundAlpha
-        ),
-        focusedSelectedColor: Color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f),
-        pressedSelectedColor: Color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f)
-    ) = ToggleableSurfaceColor(
-        color = color,
-        focusedColor = focusedColor,
-        pressedColor = pressedColor,
-        selectedColor = selectedColor,
-        disabledColor = disabledColor,
-        focusedSelectedColor = focusedSelectedColor,
-        pressedSelectedColor = pressedSelectedColor
-    )
-
-    /**
-     * Creates a [ToggleableSurfaceColor] that represents the default content colors used in a
-     * toggleable Surface.
-     *
-     * @param color the color used when the Surface is enabled, and has no other [Interaction]s.
-     * @param focusedColor the color used when the Surface is enabled and focused.
-     * @param pressedColor the color used when the Surface is enabled and pressed.
-     * @param selectedColor the color used when the Surface is enabled and selected.
-     * @param disabledColor the color used when the Surface is not enabled.
-     * @param focusedSelectedColor the color used when the Surface is enabled, focused and selected.
-     * @param pressedSelectedColor the color used when the Surface is enabled, pressed and selected.
-     */
-    @ReadOnlyComposable
-    @Composable
-    fun contentColor(
-        color: Color = MaterialTheme.colorScheme.onSurface,
-        focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
-        pressedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
-        selectedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
-        disabledColor: Color = MaterialTheme.colorScheme.onSurface,
-        focusedSelectedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
-        pressedSelectedColor: Color = MaterialTheme.colorScheme.inverseOnSurface
-    ) = ToggleableSurfaceColor(
-        color = color,
-        focusedColor = focusedColor,
-        pressedColor = pressedColor,
-        selectedColor = selectedColor,
-        disabledColor = disabledColor,
-        focusedSelectedColor = focusedSelectedColor,
-        pressedSelectedColor = pressedSelectedColor
+    fun colors(
+        containerColor: Color = MaterialTheme.colorScheme.surface,
+        contentColor: Color = contentColorFor(containerColor),
+        focusedContainerColor: Color = MaterialTheme.colorScheme.inverseSurface,
+        focusedContentColor: Color = contentColorFor(focusedContainerColor),
+        pressedContainerColor: Color = focusedContainerColor,
+        pressedContentColor: Color = contentColorFor(pressedContainerColor),
+        selectedContainerColor: Color = MaterialTheme.colorScheme.inverseSurface
+            .copy(alpha = SelectedContainerAlpha),
+        selectedContentColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
+        disabledContainerColor: Color = MaterialTheme.colorScheme.surfaceVariant
+            .copy(alpha = DisabledContainerAlpha),
+        disabledContentColor: Color = MaterialTheme.colorScheme.onSurface,
+        focusedSelectedContainerColor: Color = MaterialTheme.colorScheme.inverseSurface
+            .copy(alpha = SelectedContainerAlpha),
+        focusedSelectedContentColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
+        pressedSelectedContainerColor: Color = focusedSelectedContainerColor,
+        pressedSelectedContentColor: Color = focusedSelectedContentColor
+    ) = ToggleableSurfaceColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor,
+        selectedContainerColor = selectedContainerColor,
+        selectedContentColor = selectedContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledContentColor = disabledContentColor,
+        focusedSelectedContainerColor = focusedSelectedContainerColor,
+        focusedSelectedContentColor = focusedSelectedContentColor,
+        pressedSelectedContainerColor = pressedSelectedContainerColor,
+        pressedSelectedContentColor = pressedSelectedContentColor
     )
 
     /**
@@ -546,21 +556,39 @@
         }
     }
 
-    internal fun color(
+    internal fun containerColor(
         enabled: Boolean,
         focused: Boolean,
         pressed: Boolean,
         selected: Boolean,
-        color: ToggleableSurfaceColor
+        colors: ToggleableSurfaceColors
     ): Color {
         return when {
-            enabled && selected && pressed -> color.pressedSelectedColor
-            enabled && selected && focused -> color.focusedSelectedColor
-            enabled && selected -> color.selectedColor
-            enabled && pressed -> color.pressedColor
-            enabled && focused -> color.focusedColor
-            enabled -> color.color
-            else -> color.disabledColor
+            enabled && selected && pressed -> colors.pressedSelectedContainerColor
+            enabled && selected && focused -> colors.focusedSelectedContainerColor
+            enabled && selected -> colors.selectedContainerColor
+            enabled && pressed -> colors.pressedContainerColor
+            enabled && focused -> colors.focusedContainerColor
+            enabled -> colors.containerColor
+            else -> colors.disabledContainerColor
+        }
+    }
+
+    internal fun contentColor(
+        enabled: Boolean,
+        focused: Boolean,
+        pressed: Boolean,
+        selected: Boolean,
+        colors: ToggleableSurfaceColors
+    ): Color {
+        return when {
+            enabled && selected && pressed -> colors.pressedSelectedContentColor
+            enabled && selected && focused -> colors.focusedSelectedContentColor
+            enabled && selected -> colors.selectedContentColor
+            enabled && pressed -> colors.pressedContentColor
+            enabled && focused -> colors.focusedContentColor
+            enabled -> colors.contentColor
+            else -> colors.disabledContentColor
         }
     }
 
@@ -625,4 +653,5 @@
     }
 }
 
-private const val DisabledBackgroundAlpha = 0.4f
+private const val DisabledContainerAlpha = 0.4f
+private const val SelectedContainerAlpha = 0.5f
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceStyles.kt b/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceStyles.kt
index c8d929a..2da15cf 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceStyles.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceStyles.kt
@@ -134,38 +134,56 @@
  */
 @ExperimentalTvMaterial3Api
 @Immutable
-class ClickableSurfaceColor internal constructor(
-    internal val color: Color,
-    internal val focusedColor: Color,
-    internal val pressedColor: Color,
-    internal val disabledColor: Color
+class ClickableSurfaceColors internal constructor(
+    internal val containerColor: Color,
+    internal val contentColor: Color,
+    internal val focusedContainerColor: Color,
+    internal val focusedContentColor: Color,
+    internal val pressedContainerColor: Color,
+    internal val pressedContentColor: Color,
+    internal val disabledContainerColor: Color,
+    internal val disabledContentColor: Color
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other == null || this::class != other::class) return false
 
-        other as ClickableSurfaceColor
+        other as ClickableSurfaceColors
 
-        if (color != other.color) return false
-        if (focusedColor != other.focusedColor) return false
-        if (pressedColor != other.pressedColor) return false
-        if (disabledColor != other.disabledColor) return false
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+        if (focusedContainerColor != other.focusedContainerColor) return false
+        if (focusedContentColor != other.focusedContentColor) return false
+        if (pressedContainerColor != other.pressedContainerColor) return false
+        if (pressedContentColor != other.pressedContentColor) return false
+        if (disabledContainerColor != other.disabledContainerColor) return false
+        if (disabledContentColor != other.disabledContentColor) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = color.hashCode()
-        result = 31 * result + focusedColor.hashCode()
-        result = 31 * result + pressedColor.hashCode()
-        result = 31 * result + disabledColor.hashCode()
-
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        result = 31 * result + focusedContainerColor.hashCode()
+        result = 31 * result + focusedContentColor.hashCode()
+        result = 31 * result + pressedContainerColor.hashCode()
+        result = 31 * result + pressedContentColor.hashCode()
+        result = 31 * result + disabledContainerColor.hashCode()
+        result = 31 * result + disabledContentColor.hashCode()
         return result
     }
 
     override fun toString(): String {
-        return "ClickableSurfaceColor(color=$color, focusedColor=$focusedColor, " +
-            "pressedColor=$pressedColor, disabledColor=$disabledColor)"
+        return "ClickableSurfaceColors(" +
+            "containerColor=$containerColor, " +
+            "contentColor=$contentColor, " +
+            "focusedContainerColor=$focusedContainerColor, " +
+            "focusedContentColor=$focusedContentColor, " +
+            "pressedContainerColor=$pressedContainerColor, " +
+            "pressedContentColor=$pressedContentColor, " +
+            "disabledContainerColor=$disabledContainerColor, " +
+            "disabledContentColor=$disabledContentColor)"
     }
 }
 
@@ -174,49 +192,80 @@
  */
 @ExperimentalTvMaterial3Api
 @Immutable
-class ToggleableSurfaceColor internal constructor(
-    internal val color: Color,
-    internal val focusedColor: Color,
-    internal val pressedColor: Color,
-    internal val selectedColor: Color,
-    internal val disabledColor: Color,
-    internal val focusedSelectedColor: Color,
-    internal val pressedSelectedColor: Color
+class ToggleableSurfaceColors internal constructor(
+    internal val containerColor: Color,
+    internal val contentColor: Color,
+    internal val focusedContainerColor: Color,
+    internal val focusedContentColor: Color,
+    internal val pressedContainerColor: Color,
+    internal val pressedContentColor: Color,
+    internal val selectedContainerColor: Color,
+    internal val selectedContentColor: Color,
+    internal val disabledContainerColor: Color,
+    internal val disabledContentColor: Color,
+    internal val focusedSelectedContainerColor: Color,
+    internal val focusedSelectedContentColor: Color,
+    internal val pressedSelectedContainerColor: Color,
+    internal val pressedSelectedContentColor: Color
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other == null || this::class != other::class) return false
 
-        other as ToggleableSurfaceColor
+        other as ToggleableSurfaceColors
 
-        if (color != other.color) return false
-        if (focusedColor != other.focusedColor) return false
-        if (pressedColor != other.pressedColor) return false
-        if (selectedColor != other.selectedColor) return false
-        if (disabledColor != other.disabledColor) return false
-        if (focusedSelectedColor != other.focusedSelectedColor) return false
-        if (pressedSelectedColor != other.pressedSelectedColor) return false
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+        if (focusedContainerColor != other.focusedContainerColor) return false
+        if (focusedContentColor != other.focusedContentColor) return false
+        if (pressedContainerColor != other.pressedContainerColor) return false
+        if (pressedContentColor != other.pressedContentColor) return false
+        if (selectedContainerColor != other.selectedContainerColor) return false
+        if (selectedContentColor != other.selectedContentColor) return false
+        if (disabledContainerColor != other.disabledContainerColor) return false
+        if (disabledContentColor != other.disabledContentColor) return false
+        if (focusedSelectedContainerColor != other.focusedSelectedContainerColor) return false
+        if (focusedSelectedContentColor != other.focusedSelectedContentColor) return false
+        if (pressedSelectedContainerColor != other.pressedSelectedContainerColor) return false
+        if (pressedSelectedContentColor != other.pressedSelectedContentColor) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = color.hashCode()
-        result = 31 * result + focusedColor.hashCode()
-        result = 31 * result + pressedColor.hashCode()
-        result = 31 * result + selectedColor.hashCode()
-        result = 31 * result + disabledColor.hashCode()
-        result = 31 * result + focusedSelectedColor.hashCode()
-        result = 31 * result + pressedSelectedColor.hashCode()
-
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        result = 31 * result + focusedContainerColor.hashCode()
+        result = 31 * result + focusedContentColor.hashCode()
+        result = 31 * result + pressedContainerColor.hashCode()
+        result = 31 * result + pressedContentColor.hashCode()
+        result = 31 * result + selectedContainerColor.hashCode()
+        result = 31 * result + selectedContentColor.hashCode()
+        result = 31 * result + disabledContainerColor.hashCode()
+        result = 31 * result + disabledContentColor.hashCode()
+        result = 31 * result + focusedSelectedContainerColor.hashCode()
+        result = 31 * result + focusedSelectedContentColor.hashCode()
+        result = 31 * result + pressedSelectedContainerColor.hashCode()
+        result = 31 * result + pressedSelectedContentColor.hashCode()
         return result
     }
 
     override fun toString(): String {
-        return "ToggleableSurfaceColor(color=$color, focusedColor=$focusedColor," +
-            "pressedColor=$pressedColor, selectedColor=$selectedColor," +
-            "disabledColor=$disabledColor, focusedSelectedColor=$focusedSelectedColor, " +
-            "pressedSelectedColor=$pressedSelectedColor)"
+        return "ToggleableSurfaceColors(" +
+            "containerColor=$containerColor, " +
+            "contentColor=$contentColor, " +
+            "focusedContainerColor=$focusedContainerColor, " +
+            "focusedContentColor=$focusedContentColor, " +
+            "pressedContainerColor=$pressedContainerColor, " +
+            "pressedContentColor=$pressedContentColor, " +
+            "selectedContainerColor=$selectedContainerColor, " +
+            "selectedContentColor=$selectedContentColor, " +
+            "disabledContainerColor=$disabledContainerColor, " +
+            "disabledContentColor=$disabledContentColor, " +
+            "focusedSelectedContainerColor=$focusedSelectedContainerColor, " +
+            "focusedSelectedContentColor=$focusedSelectedContentColor, " +
+            "pressedSelectedContainerColor=$pressedSelectedContainerColor, " +
+            "pressedSelectedContentColor=$pressedSelectedContentColor)"
     }
 }
 
@@ -381,8 +430,10 @@
     }
 
     override fun toString(): String {
-        return "${this.javaClass.simpleName}(border=$border, focusedBorder=$focusedBorder, " +
-            "pressedBorder=$pressedBorder, disabledBorder=$disabledBorder, " +
+        return "ClickableSurfaceBorder(border=$border, " +
+            "focusedBorder=$focusedBorder, " +
+            "pressedBorder=$pressedBorder, " +
+            "disabledBorder=$disabledBorder, " +
             "focusedDisabledBorder=$focusedDisabledBorder)"
     }
 }
@@ -533,3 +584,36 @@
             "focusedSelectedGlow=$focusedSelectedGlow, pressedSelectedGlow=$pressedSelectedGlow)"
     }
 }
+
+/**
+ * Defines the container & content color [Color] for a non interactive surface.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class NonInteractiveSurfaceColors internal constructor(
+    internal val containerColor: Color,
+    internal val contentColor: Color
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as NonInteractiveSurfaceColors
+
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "NonInteractiveSurfaceColors(containerColor=$containerColor, " +
+            "contentColor=$contentColor)"
+    }
+}
\ No newline at end of file
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Switch.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Switch.kt
index 936056a..d71168a 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Switch.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Switch.kt
@@ -66,6 +66,8 @@
  *
  * Switch can be used with a custom icon via [thumbContent] parameter
  *
+ * @sample androidx.tv.samples.SwitchSample
+ *
  * @param checked whether or not this switch is checked
  * @param onCheckedChange called when this switch is clicked. If `null`, then this switch will not
  * be interactable, unless something else handles its input events and updates its state.
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.kt b/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.kt
index 30f4d67..5e59fbc 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/WideButton.kt
@@ -35,7 +35,6 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.onPlaced
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
@@ -48,6 +47,9 @@
 /**
  * Material Design wide button for TV.
  *
+ * Samples:
+ * @sample androidx.tv.samples.WideButtonSample
+ *
  * @param onClick called when this button is clicked
  * @param modifier the [Modifier] to be applied to this button
  * @param enabled controls the enabled state of this button. When `false`, this component will not
@@ -111,6 +113,11 @@
 /**
  * Material Design wide button for TV.
  *
+ * Samples:
+ * @sample androidx.tv.samples.WideButtonWithIcon
+ * @sample androidx.tv.samples.WideButtonWithSubtitle
+ * @sample androidx.tv.samples.WideButtonWithIconAndSubtitle
+ *
  * @param onClick called when this button is clicked
  * @param title the title content of the button, typically a [Text]
  * @param modifier the [Modifier] to be applied to this button
@@ -238,8 +245,7 @@
         scale = scale.toClickableSurfaceScale(),
         glow = glow.toClickableSurfaceGlow(),
         shape = shape.toClickableSurfaceShape(),
-        color = wideButtonContainerColor(),
-        contentColor = contentColor.toClickableSurfaceContentColor(),
+        colors = contentColor.toClickableSurfaceColors(),
         tonalElevation = tonalElevation,
         border = border.toClickableSurfaceBorder(),
         interactionSource = interactionSource
@@ -273,12 +279,3 @@
         }
     }
 }
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-private fun wideButtonContainerColor() = ClickableSurfaceDefaults.color(
-    color = Color.Transparent,
-    focusedColor = Color.Transparent,
-    pressedColor = Color.Transparent,
-    disabledColor = Color.Transparent,
-)
diff --git a/vectordrawable/vectordrawable-animated/src/main/AndroidManifest.xml b/vectordrawable/vectordrawable-animated/src/main/AndroidManifest.xml
deleted file mode 100644
index 3bae3b9..0000000
--- a/vectordrawable/vectordrawable-animated/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-   Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-  -->
-<manifest />
diff --git a/vectordrawable/vectordrawable-seekable/src/androidTest/AndroidManifest.xml b/vectordrawable/vectordrawable-seekable/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 4de3d50..0000000
--- a/vectordrawable/vectordrawable-seekable/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
--->
-<manifest />
diff --git a/vectordrawable/vectordrawable-seekable/src/main/AndroidManifest.xml b/vectordrawable/vectordrawable-seekable/src/main/AndroidManifest.xml
deleted file mode 100644
index 4de3d50..0000000
--- a/vectordrawable/vectordrawable-seekable/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
--->
-<manifest />
diff --git a/vectordrawable/vectordrawable/src/main/AndroidManifest.xml b/vectordrawable/vectordrawable/src/main/AndroidManifest.xml
deleted file mode 100644
index 3bae3b9..0000000
--- a/vectordrawable/vectordrawable/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-   Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-  -->
-<manifest />
diff --git a/viewpager/viewpager/src/main/AndroidManifest.xml b/viewpager/viewpager/src/main/AndroidManifest.xml
deleted file mode 100644
index dd3d3a3..0000000
--- a/viewpager/viewpager/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/viewpager2/integration-tests/targetsdk-tests/src/main/AndroidManifest.xml b/viewpager2/integration-tests/targetsdk-tests/src/main/AndroidManifest.xml
deleted file mode 100644
index a270226..0000000
--- a/viewpager2/integration-tests/targetsdk-tests/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
\ No newline at end of file
diff --git a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
index 79e4f0d..b3e2d09 100644
--- a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
@@ -42,6 +42,7 @@
 import org.hamcrest.Matchers.greaterThan
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Assume.assumeThat
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -128,6 +129,7 @@
     }
 
     @Test
+    @Ignore("b/280670752")
     fun test_peekNextAndMoveBack() {
         // Roughly interpolates like this:
         //   |
diff --git a/viewpager2/viewpager2/src/main/AndroidManifest.xml b/viewpager2/viewpager2/src/main/AndroidManifest.xml
deleted file mode 100644
index b6d5627..0000000
--- a/viewpager2/viewpager2/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest />
\ No newline at end of file
diff --git a/wear/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/wear/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/benchmark/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/wear/benchmark/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index f734545..0000000
--- a/wear/benchmark/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/wear/compose/compose-foundation/benchmark/src/androidTest/AndroidManifest.xml b/wear/compose/compose-foundation/benchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 39f7fc3..0000000
--- a/wear/compose/compose-foundation/benchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
diff --git a/wear/compose/compose-foundation/src/androidMain/AndroidManifest.xml b/wear/compose/compose-foundation/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/compose/compose-foundation/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/compose/compose-foundation/src/androidMain/kotlin/androidx/wear/compose/foundation/CurvedTextDelegate.android.kt b/wear/compose/compose-foundation/src/androidMain/kotlin/androidx/wear/compose/foundation/CurvedTextDelegate.android.kt
index d781ba1..fcc21e2 100644
--- a/wear/compose/compose-foundation/src/androidMain/kotlin/androidx/wear/compose/foundation/CurvedTextDelegate.android.kt
+++ b/wear/compose/compose-foundation/src/androidMain/kotlin/androidx/wear/compose/foundation/CurvedTextDelegate.android.kt
@@ -24,6 +24,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -50,8 +51,8 @@
     private var clockwise: Boolean = true
     private var fontSizePx: Float = 0f
 
-    actual var textWidth by mutableStateOf(0f)
-    actual var textHeight by mutableStateOf(0f)
+    actual var textWidth by mutableFloatStateOf(0f)
+    actual var textHeight by mutableFloatStateOf(0f)
     actual var baseLinePosition = 0f
 
     private var typeFace: State<Typeface?> = mutableStateOf(null)
diff --git a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeToReveal.kt b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeToReveal.kt
index 50764ab..724c367 100644
--- a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeToReveal.kt
+++ b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeToReveal.kt
@@ -32,7 +32,7 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -360,7 +360,7 @@
     /**
      * The total width of the overlay content in float.
      */
-    val width = mutableStateOf(0.0f)
+    val width = mutableFloatStateOf(0.0f)
 
     override val revealOffset: Float
         get() = width.value * (revealState.swipeAnchors[RevealValue.Revealing] ?: 0.0f)
diff --git a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeableV2.kt b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeableV2.kt
index 788ff21..273c59b 100644
--- a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeableV2.kt
+++ b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/SwipeableV2.kt
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -273,7 +274,7 @@
      * You can use this value to provide smooth reconciliation behavior when re-targeting an
      * animation.
      */
-    var lastVelocity: Float by mutableStateOf(0f)
+    var lastVelocity: Float by mutableFloatStateOf(0f)
         private set
 
     /**
diff --git a/wear/compose/compose-material-core/src/androidMain/AndroidManifest.xml b/wear/compose/compose-material-core/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/compose/compose-material-core/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Card.kt b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Card.kt
new file mode 100644
index 0000000..7994fe3
--- /dev/null
+++ b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Card.kt
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import androidx.annotation.RestrictTo
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.paint
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+
+/**
+ * Base level Wear Material [Card] that offers a single slot to take any content.
+ *
+ * Is used as the container for more opinionated [Card] components that take specific content such
+ * as icons, images, titles, subtitles and labels.
+ *
+ * Cards can be enabled or disabled. A disabled card will not respond to click events.
+ *
+ * For more information, see the
+ * [Cards](https://developer.android.com/training/wearables/components/cards)
+ * Wear OS Material design guide.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param modifier Modifier to be applied to the card
+ * @param border A BorderStroke object which is used for the outline drawing.
+ * Can be null - then outline will not be drawn
+ * @param containerPainter A painter used to paint the background of the card. A card will
+ * normally have a gradient background. Use [CardDefaults.cardBackgroundPainter()] to obtain an
+ * appropriate painter
+ * @param containerPainter A Painter which is used for background drawing.
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param role The type of user interface element. Accessibility services might use this
+ * to describe the element or do customizations
+ * @param content A main slot for a content of this card
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+public fun Card(
+    onClick: () -> Unit,
+    modifier: Modifier,
+    border: BorderStroke?,
+    containerPainter: Painter,
+    enabled: Boolean,
+    contentPadding: PaddingValues,
+    shape: Shape,
+    interactionSource: MutableInteractionSource,
+    role: Role?,
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    Column(
+        modifier = modifier
+            .fillMaxWidth()
+            .height(IntrinsicSize.Min)
+            .clip(shape = shape)
+            .paint(
+                painter = containerPainter,
+                contentScale = ContentScale.Crop
+            )
+            .clickable(
+                enabled = enabled,
+                onClick = onClick,
+                role = role,
+                indication = rememberRipple(),
+                interactionSource = interactionSource,
+            )
+            .then(
+                border?.let { Modifier.border(border = border, shape = shape) } ?: Modifier
+            )
+            .padding(contentPadding),
+        content = content
+    )
+}
+
+/**
+ * Opinionated Wear Material [Card] that offers a specific 5 slot layout to show information about
+ * an application, e.g. a notification. AppCards are designed to show interactive elements from
+ * multiple applications. They will typically be used by the system UI, e.g. for showing a list of
+ * notifications from different applications. However it could also be adapted by individual
+ * application developers to show information about different parts of their application.
+ *
+ * The first row of the layout has three slots, 1) a small optional application [Image] or [Icon],
+ * 2) an application name, it is expected to be a short start-aligned [Text] composable,
+ * and 3) the optional time that the application activity has occurred which will be
+ * shown on the top row of the card, this is expected to be an end aligned [Text] composable
+ * showing a time relevant to the contents of the [Card].
+ *
+ * The second row shows a title, this is expected to be a single row of start aligned [Text].
+ *
+ * The rest of the [Card] contains the content which can be either [Text] or an [Image].
+ * If the content is text it can be single or multiple line and is expected to be Top and Start
+ * aligned.
+ *
+ * If more than one composable is provided in the content slot it is the responsibility of the
+ * caller to determine how to layout the contents, e.g. provide either a row or a column.
+ *
+ * For more information, see the
+ * [Cards](https://developer.android.com/training/wearables/components/cards)
+ * guide.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param modifier Modifier to be applied to the card
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param border A BorderStroke object which is used for the outline drawing.
+ * Can be null - then outline will not be drawn
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param containerPainter A Painter which is used for background drawing.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param appImage A slot for a small [Image] associated with the application.
+ * @param appName A slot for displaying the application name, expected to be a single line of start
+ * aligned text.
+ * @param time A slot for displaying the time relevant to the contents of the card, expected to be a
+ * short piece of end aligned text.
+ * @param title A slot for displaying the title of the card, expected to be one or two lines of
+ * start aligned text.
+ * @param content A main slot for a content of this card
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+public fun AppCard(
+    onClick: () -> Unit,
+    modifier: Modifier,
+    enabled: Boolean,
+    border: BorderStroke?,
+    contentPadding: PaddingValues,
+    containerPainter: Painter,
+    interactionSource: MutableInteractionSource,
+    shape: Shape,
+    appImage: @Composable (RowScope.() -> Unit)?,
+    appName: @Composable RowScope.() -> Unit,
+    time: @Composable (RowScope.() -> Unit)?,
+    title: @Composable RowScope.() -> Unit,
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    Card(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        containerPainter = containerPainter,
+        border = border,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        role = null,
+        shape = shape
+    ) {
+        Column {
+            Row(
+                modifier = Modifier.fillMaxWidth(),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                appImage?.let {
+                    appImage()
+                    Spacer(Modifier.width(6.dp))
+                }
+                appName()
+                Spacer(modifier = Modifier.weight(1.0f))
+
+                time?.let {
+                    time()
+                }
+            }
+            Spacer(modifier = Modifier.height(4.dp))
+            Row(
+                modifier = Modifier.fillMaxWidth(),
+                verticalAlignment = Alignment.CenterVertically,
+                content = title
+            )
+            content()
+        }
+    }
+}
+
+/**
+ * Opinionated Wear Material [Card] that offers a specific 3 slot layout to show interactive
+ * information about an application, e.g. a message. TitleCards are designed for use within an
+ * application.
+ *
+ * The first row of the layout has two slots. 1. a start aligned title. The title text is
+ * expected to be a maximum of 2 lines of text.
+ * 2. An optional time that the application activity has occurred shown at the
+ * end of the row, expected to be an end aligned [Text] composable showing a time relevant to the
+ * contents of the [Card].
+ *
+ * The rest of the [Card] contains the content which is expected to be [Text] or a contained
+ * [Image].
+ *
+ * If the content is text it can be single or multiple line and is expected to be Top and Start
+ * aligned.
+ *
+ * Overall the [title] and [content] text should be no more than 5 rows of text combined.
+ *
+ * If more than one composable is provided in the content slot it is the responsibility of the
+ * caller to determine how to layout the contents, e.g. provide either a row or a column.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param modifier Modifier to be applied to the card
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param border A BorderStroke object which is used for the outline drawing.
+ * Can be null - then outline will not be drawn
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param containerPainter A Painter which is used for background drawing.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param title A slot for displaying the title of the card, expected to be one or two
+ * lines of text.
+ * @param time An optional slot for displaying the time relevant to the contents of the card,
+ * expected to be a short piece of end aligned text.
+ * @param content A main slot for a content of this card.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+public fun TitleCard(
+    onClick: () -> Unit,
+    modifier: Modifier,
+    enabled: Boolean,
+    border: BorderStroke?,
+    contentPadding: PaddingValues,
+    containerPainter: Painter,
+    interactionSource: MutableInteractionSource,
+    shape: Shape,
+    title: @Composable RowScope.() -> Unit,
+    time: @Composable (RowScope.() -> Unit)?,
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    Card(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        containerPainter = containerPainter,
+        border = border,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        role = null,
+        shape = shape
+    ) {
+        Column {
+            Row(
+                modifier = Modifier.fillMaxWidth(),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                title()
+                time?.let {
+                    Spacer(modifier = Modifier.weight(1.0f))
+                    time()
+                }
+            }
+            content()
+        }
+    }
+}
diff --git a/wear/compose/compose-material/benchmark/src/androidTest/AndroidManifest.xml b/wear/compose/compose-material/benchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 39f7fc3..0000000
--- a/wear/compose/compose-material/benchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
\ No newline at end of file
diff --git a/wear/compose/compose-material/samples/src/androidTest/AndroidManifest.xml b/wear/compose/compose-material/samples/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 8e90956..0000000
--- a/wear/compose/compose-material/samples/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/wear/compose/compose-material/src/androidMain/AndroidManifest.xml b/wear/compose/compose-material/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/compose/compose-material/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultTimeSource.kt b/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultTimeSource.kt
index 5f3393d..9ee204f 100644
--- a/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultTimeSource.kt
+++ b/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultTimeSource.kt
@@ -27,6 +27,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableLongStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -50,7 +51,7 @@
 ): State<String> {
 
     var calendar by remember { mutableStateOf(Calendar.getInstance()) }
-    var currentTime by remember { mutableStateOf(time()) }
+    var currentTime by remember { mutableLongStateOf(time()) }
 
     val timeText = remember {
         derivedStateOf { formatTime(calendar, currentTime, timeFormat) }
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt
index 596a2f2..2b09353 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt
@@ -17,29 +17,18 @@
 package androidx.wear.compose.material
 
 import androidx.compose.foundation.Image
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.IntrinsicSize
 import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.draw.paint
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Brush
@@ -53,7 +42,6 @@
 import androidx.compose.ui.graphics.painter.BrushPainter
 import androidx.compose.ui.graphics.painter.ColorPainter
 import androidx.compose.ui.graphics.painter.Painter
-import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.Dp
@@ -113,23 +101,16 @@
     role: Role? = null,
     content: @Composable ColumnScope.() -> Unit,
 ) {
-    Column(
-        modifier = modifier
-            .fillMaxWidth()
-            .height(IntrinsicSize.Min)
-            .clip(shape = shape)
-            .paint(
-                painter = backgroundPainter,
-                contentScale = ContentScale.Crop
-            )
-            .clickable(
-                enabled = enabled,
-                onClick = onClick,
-                role = role,
-                indication = rememberRipple(),
-                interactionSource = interactionSource,
-            )
-            .padding(contentPadding)
+    androidx.wear.compose.materialcore.Card(
+        onClick = onClick,
+        modifier = modifier,
+        border = null,
+        containerPainter = backgroundPainter,
+        enabled = enabled,
+        contentPadding = contentPadding,
+        shape = shape,
+        interactionSource = interactionSource,
+        role = role,
     ) {
         CompositionLocalProvider(
             LocalContentColor provides contentColor,
@@ -208,53 +189,46 @@
     titleColor: Color = MaterialTheme.colors.onSurface,
     content: @Composable ColumnScope.() -> Unit,
 ) {
-    Card(
+    androidx.wear.compose.materialcore.AppCard(
         onClick = onClick,
         modifier = modifier,
-        backgroundPainter = backgroundPainter,
         enabled = enabled,
-    ) {
-        Column {
-            Row(
-                modifier = Modifier.fillMaxWidth(),
-                verticalAlignment = Alignment.CenterVertically
-            ) {
-                CompositionLocalProvider(
-                    LocalTextStyle provides MaterialTheme.typography.caption1
-                ) {
-                    if (appImage != null) {
-                        appImage()
-                        Spacer(modifier = Modifier.width(6.dp))
-                    }
-                    CompositionLocalProvider(
-                        LocalContentColor provides appColor,
-                    ) {
-                        appName()
-                    }
-                }
-                Spacer(modifier = Modifier.weight(1.0f))
-                CompositionLocalProvider(
-                    LocalContentColor provides timeColor,
-                    LocalTextStyle provides MaterialTheme.typography.caption1,
-                ) {
-                    time()
-                }
-            }
-            Spacer(modifier = Modifier.height(4.dp))
-            Row {
-                CompositionLocalProvider(
-                    LocalContentColor provides titleColor,
-                    LocalTextStyle provides MaterialTheme.typography.title3,
-                ) {
-                    title()
-                }
-            }
+        border = null,
+        contentPadding = CardDefaults.ContentPadding,
+        containerPainter = backgroundPainter,
+        interactionSource = remember { MutableInteractionSource() },
+        shape = MaterialTheme.shapes.large,
+        appImage = appImage?.let { { appImage() } },
+        appName = {
             CompositionLocalProvider(
-                LocalContentColor provides contentColor,
-                LocalTextStyle provides MaterialTheme.typography.body1,
+                LocalContentColor provides appColor,
+                LocalTextStyle provides MaterialTheme.typography.caption1
             ) {
-                content()
+                appName()
             }
+        },
+        time = {
+            CompositionLocalProvider(
+                LocalContentColor provides timeColor,
+                LocalTextStyle provides MaterialTheme.typography.caption1,
+            ) {
+                time()
+            }
+        },
+        title = {
+            CompositionLocalProvider(
+                LocalContentColor provides titleColor,
+                LocalTextStyle provides MaterialTheme.typography.title3
+            ) {
+                title()
+            }
+        }
+    ) {
+        CompositionLocalProvider(
+            LocalContentColor provides contentColor,
+            LocalTextStyle provides MaterialTheme.typography.body1,
+        ) {
+            content()
         }
     }
 }
@@ -321,33 +295,35 @@
     timeColor: Color = contentColor,
     content: @Composable ColumnScope.() -> Unit,
 ) {
-    Card(
+    androidx.wear.compose.materialcore.TitleCard(
         onClick = onClick,
         modifier = modifier,
-        backgroundPainter = backgroundPainter,
         enabled = enabled,
-    ) {
-        Column {
-            Row(
-                modifier = Modifier.fillMaxWidth(),
-                verticalAlignment = Alignment.CenterVertically
+        border = null,
+        contentPadding = CardDefaults.ContentPadding,
+        containerPainter = backgroundPainter,
+        interactionSource = remember { MutableInteractionSource() },
+        shape = MaterialTheme.shapes.large,
+        title = {
+            CompositionLocalProvider(
+                LocalContentColor provides titleColor,
+                LocalTextStyle provides MaterialTheme.typography.title3,
             ) {
+                title()
+            }
+        },
+        time = {
+            time?.let {
+                Spacer(modifier = Modifier.weight(1.0f))
                 CompositionLocalProvider(
-                    LocalContentColor provides titleColor,
-                    LocalTextStyle provides MaterialTheme.typography.title3,
+                    LocalContentColor provides timeColor,
+                    LocalTextStyle provides MaterialTheme.typography.caption1,
                 ) {
-                    title()
-                }
-                if (time != null) {
-                    Spacer(modifier = Modifier.weight(1.0f))
-                    CompositionLocalProvider(
-                        LocalContentColor provides timeColor,
-                        LocalTextStyle provides MaterialTheme.typography.caption1,
-                    ) {
-                        time()
-                    }
+                    time()
                 }
             }
+        },
+        content = {
             Spacer(modifier = Modifier.height(2.dp))
             CompositionLocalProvider(
                 LocalContentColor provides contentColor,
@@ -356,7 +332,7 @@
                 content()
             }
         }
-    }
+    )
 }
 
 /**
@@ -387,12 +363,14 @@
                 .compositeOver(MaterialTheme.colors.background),
         gradientDirection: LayoutDirection = LocalLayoutDirection.current
     ): Painter {
-        return BrushPainter(FortyFiveDegreeLinearGradient(
-            colors = listOf(
-                startBackgroundColor,
-                endBackgroundColor
-            ),
-            ltr = gradientDirection == LayoutDirection.Ltr)
+        return BrushPainter(
+            FortyFiveDegreeLinearGradient(
+                colors = listOf(
+                    startBackgroundColor,
+                    endBackgroundColor
+                ),
+                ltr = gradientDirection == LayoutDirection.Ltr
+            )
         )
     }
 
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt
index 963051d..1615159 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt
@@ -38,6 +38,7 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -486,7 +487,7 @@
         verifyNumberOfOptions(initialNumberOfOptions)
     }
 
-    private var _numberOfOptions by mutableStateOf(initialNumberOfOptions)
+    private var _numberOfOptions by mutableIntStateOf(initialNumberOfOptions)
 
     var numberOfOptions
         get() = _numberOfOptions
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt
index d31497f..6ddae0d4 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt
@@ -25,7 +25,7 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.saveable.listSaver
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
@@ -199,7 +199,7 @@
     /**
      * The current selected [Picker] index.
      */
-    var selectedIndex by mutableStateOf(initiallySelectedIndex)
+    var selectedIndex by mutableIntStateOf(initiallySelectedIndex)
 
     public companion object {
         val Saver = listSaver<PickerGroupState, Any?>(
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
index ed29bf9..5430358 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -108,7 +109,7 @@
 ) {
     // Will be updated in onSizeChanged, initialise to any value other than zero
     // so that it is different to the other anchor used for the swipe gesture.
-    var maxWidth by remember { mutableStateOf(1f) }
+    var maxWidth by remember { mutableFloatStateOf(1f) }
     Box(
         modifier = modifier
             .fillMaxSize()
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt
index 52ed98b..9a6fda0 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt
@@ -203,7 +203,7 @@
 
     internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f })
 
-    internal var velocityThreshold by mutableStateOf(0f)
+    internal var velocityThreshold by mutableFloatStateOf(0f)
 
     internal var resistance: ResistanceConfig? by mutableStateOf(null)
 
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index cf3b196..ffc5824 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -2,6 +2,8 @@
 package androidx.wear.compose.material3 {
 
   @androidx.compose.runtime.Immutable public final class ButtonColors {
+    ctor public ButtonColors(androidx.compose.ui.graphics.painter.Painter containerPainter, long contentColor, long secondaryContentColor, long iconColor, androidx.compose.ui.graphics.painter.Painter disabledContainerPainter, long disabledContentColor, long disabledSecondaryContentColor, long disabledIconColor);
+    ctor public ButtonColors(long containerColor, long contentColor, long secondaryContentColor, long iconColor, long disabledContainerColor, long disabledContentColor, long disabledSecondaryContentColor, long disabledIconColor);
   }
 
   public final class ButtonDefaults {
@@ -22,14 +24,38 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardColors {
+    ctor public CardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, long contentColor, long appNameColor, long timeColor, long titleColor);
+  }
+
+  public final class CardDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    method public float getAppImageSize();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors imageCardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional long outlineColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors(optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    property public final float AppImageSize;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    field public static final androidx.wear.compose.material3.CardDefaults INSTANCE;
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable public static void AppCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> appName, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? appImage, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TitleCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable public final class ColorScheme {
@@ -116,6 +142,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class IconButtonColors {
+    ctor public IconButtonColors(long containerColor, long contentColor, long disabledContainerColor, long disabledContentColor);
   }
 
   public final class IconButtonDefaults {
diff --git a/wear/compose/compose-material3/api/public_plus_experimental_current.txt b/wear/compose/compose-material3/api/public_plus_experimental_current.txt
index a23926c..bbe675a 100644
--- a/wear/compose/compose-material3/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material3/api/public_plus_experimental_current.txt
@@ -2,6 +2,8 @@
 package androidx.wear.compose.material3 {
 
   @androidx.compose.runtime.Immutable public final class ButtonColors {
+    ctor public ButtonColors(androidx.compose.ui.graphics.painter.Painter containerPainter, long contentColor, long secondaryContentColor, long iconColor, androidx.compose.ui.graphics.painter.Painter disabledContainerPainter, long disabledContentColor, long disabledSecondaryContentColor, long disabledIconColor);
+    ctor public ButtonColors(long containerColor, long contentColor, long secondaryContentColor, long iconColor, long disabledContainerColor, long disabledContentColor, long disabledSecondaryContentColor, long disabledIconColor);
   }
 
   public final class ButtonDefaults {
@@ -22,14 +24,38 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardColors {
+    ctor public CardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, long contentColor, long appNameColor, long timeColor, long titleColor);
+  }
+
+  public final class CardDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    method public float getAppImageSize();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors imageCardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional long outlineColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors(optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    property public final float AppImageSize;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    field public static final androidx.wear.compose.material3.CardDefaults INSTANCE;
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable public static void AppCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> appName, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? appImage, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TitleCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable public final class ColorScheme {
@@ -119,6 +145,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class IconButtonColors {
+    ctor public IconButtonColors(long containerColor, long contentColor, long disabledContainerColor, long disabledContentColor);
   }
 
   public final class IconButtonDefaults {
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index cf3b196..ffc5824 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -2,6 +2,8 @@
 package androidx.wear.compose.material3 {
 
   @androidx.compose.runtime.Immutable public final class ButtonColors {
+    ctor public ButtonColors(androidx.compose.ui.graphics.painter.Painter containerPainter, long contentColor, long secondaryContentColor, long iconColor, androidx.compose.ui.graphics.painter.Painter disabledContainerPainter, long disabledContentColor, long disabledSecondaryContentColor, long disabledIconColor);
+    ctor public ButtonColors(long containerColor, long contentColor, long secondaryContentColor, long iconColor, long disabledContainerColor, long disabledContentColor, long disabledSecondaryContentColor, long disabledIconColor);
   }
 
   public final class ButtonDefaults {
@@ -22,14 +24,38 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
-    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.semantics.Role? role);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ChildButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? secondaryLabel, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.ButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardColors {
+    ctor public CardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, long contentColor, long appNameColor, long timeColor, long titleColor);
+  }
+
+  public final class CardDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    method public float getAppImageSize();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors imageCardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional long outlineColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors(optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor);
+    property public final float AppImageSize;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    field public static final androidx.wear.compose.material3.CardDefaults INSTANCE;
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable public static void AppCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> appName, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? appImage, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TitleCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable public final class ColorScheme {
@@ -116,6 +142,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class IconButtonColors {
+    ctor public IconButtonColors(long containerColor, long contentColor, long disabledContainerColor, long disabledContentColor);
   }
 
   public final class IconButtonDefaults {
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt
new file mode 100644
index 0000000..6684dec
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.test
+
+import android.os.Build
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import androidx.wear.compose.material3.AppCard
+import androidx.wear.compose.material3.Card
+import androidx.wear.compose.material3.CardColors
+import androidx.wear.compose.material3.CardDefaults
+import androidx.wear.compose.material3.OutlinedCard
+import androidx.wear.compose.material3.SCREENSHOT_GOLDEN_PATH
+import androidx.wear.compose.material3.TEST_TAG
+import androidx.wear.compose.material3.TestIcon
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.TitleCard
+import androidx.wear.compose.material3.setContentWithTheme
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class CardScreenshotTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+    @get:Rule
+    val testName = TestName()
+
+    @Test
+    fun card_ltr() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleCard()
+    }
+
+    @Test
+    fun card_disabled() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleCard(enabled = false)
+    }
+
+    @Test
+    fun card_rtl() = verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+        sampleCard()
+    }
+
+    @Test
+    fun card_image_background() = verifyScreenshot {
+        sampleCard(
+            colors = CardDefaults.imageCardColors(
+                containerPainter = CardDefaults.imageWithScrimBackgroundPainter(
+                    backgroundImagePainter = painterResource(id = R.drawable.backgroundimage1)
+                )
+            )
+        )
+    }
+
+    @Test
+    fun outlined_card_ltr() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleOutlinedCard()
+    }
+
+    @Test
+    fun outlined_card_disabled() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleOutlinedCard(enabled = false)
+    }
+
+    @Test
+    fun outlined_card_rtl() = verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+        sampleOutlinedCard()
+    }
+
+    @Test
+    fun app_card_ltr() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleAppCard()
+    }
+
+    @Test
+    fun app_card_disabled() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleAppCard(enabled = false)
+    }
+
+    @Test
+    fun app_card_rtl() = verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+        sampleAppCard()
+    }
+
+    @Test
+    fun app_card_image_background() = verifyScreenshot {
+        sampleAppCard(
+            colors = CardDefaults.imageCardColors(
+                containerPainter = CardDefaults.imageWithScrimBackgroundPainter(
+                    backgroundImagePainter = painterResource(id = R.drawable.backgroundimage1)
+                )
+            )
+        )
+    }
+
+    @Test
+    fun title_card_ltr() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleTitleCard()
+    }
+
+    @Test
+    fun title_card_disabled() = verifyScreenshot(layoutDirection = LayoutDirection.Ltr) {
+        sampleTitleCard(enabled = false)
+    }
+
+    @Test
+    fun title_card_rtl() = verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+        sampleTitleCard()
+    }
+
+    @Test
+    fun title_card_image_background() = verifyScreenshot {
+        sampleTitleCard(
+            colors = CardDefaults.imageCardColors(
+                containerPainter = CardDefaults.imageWithScrimBackgroundPainter(
+                    backgroundImagePainter = painterResource(id = R.drawable.backgroundimage1)
+                )
+            )
+        )
+    }
+
+    @Composable
+    private fun sampleCard(
+        enabled: Boolean = true,
+        colors: CardColors = CardDefaults.cardColors()
+    ) {
+        Card(
+            enabled = enabled,
+            onClick = {},
+            colors = colors,
+            modifier = Modifier.testTag(TEST_TAG),
+        ) {
+            Text("Card: Some body content")
+        }
+    }
+
+    @Composable
+    private fun sampleOutlinedCard(
+        enabled: Boolean = true,
+    ) {
+        OutlinedCard(
+            enabled = enabled,
+            onClick = {},
+            modifier = Modifier.testTag(TEST_TAG),
+        ) {
+            Text("Outlined Card: Some body content")
+        }
+    }
+
+    @Composable
+    private fun sampleAppCard(
+        enabled: Boolean = true,
+        colors: CardColors = CardDefaults.cardColors()
+    ) {
+        AppCard(
+            enabled = enabled,
+            onClick = {},
+            appName = { Text("AppName") },
+            appImage = { TestIcon() },
+            title = { Text("AppCard") },
+            colors = colors,
+            time = { Text("now") },
+            modifier = Modifier.testTag(TEST_TAG),
+        ) {
+            Text("Some body content")
+            Text("and some more body content")
+        }
+    }
+
+    @Composable
+    private fun sampleTitleCard(
+        enabled: Boolean = true,
+        colors: CardColors = CardDefaults.cardColors()
+    ) {
+        TitleCard(
+            enabled = enabled,
+            onClick = {},
+            title = { Text("TitleCard") },
+            time = { Text("now") },
+            colors = colors,
+            modifier = Modifier.testTag(TEST_TAG),
+        ) {
+            Text("Some body content")
+            Text("and some more body content")
+        }
+    }
+
+    private fun verifyScreenshot(
+        layoutDirection: LayoutDirection = LayoutDirection.Ltr,
+        content: @Composable () -> Unit
+    ) {
+        rule.setContentWithTheme {
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                content()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, testName.methodName)
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/CardTest.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/CardTest.kt
new file mode 100644
index 0000000..6b01051
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/CardTest.kt
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+public class CardTest {
+    @get:Rule
+    public val rule: ComposeContentTestRule = createComposeRule()
+
+    @Test
+    public fun supports_test_tag() {
+        rule.setContentWithTheme {
+            Card(
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    public fun has_clickaction_when_enabled() {
+        rule.setContentWithTheme {
+            Card(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    public fun has_clickaction_when_disabled() {
+        rule.setContentWithTheme {
+            Card(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    public fun is_correctly_enabled_when_enabled_equals_true() {
+        rule.setContentWithTheme {
+            Card(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    public fun is_correctly_disabled_when_enabled_equals_false() {
+        rule.setContentWithTheme {
+            Card(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    public fun responds_to_click_when_enabled() {
+        var clicked = false
+
+        rule.setContentWithTheme {
+            Card(
+                onClick = { clicked = true },
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performClick()
+
+        rule.runOnIdle {
+            assertEquals(true, clicked)
+        }
+    }
+
+    @Test
+    public fun does_not_respond_to_click_when_disabled() {
+        var clicked = false
+
+        rule.setContentWithTheme {
+            Card(
+                onClick = { clicked = true },
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performClick()
+
+        rule.runOnIdle {
+            assertEquals(false, clicked)
+        }
+    }
+
+    @Test
+    public fun has_role_button_if_explicitly_set() {
+        rule.setContentWithTheme {
+            Card(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TEST_TAG)
+                    .semantics { role = Role.Button },
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Button
+                )
+            )
+    }
+
+    @Test
+    public fun gives_base_card_correct_default_max_height(): Unit =
+        verifyHeight(
+            expectedHeight = 100.dp +
+                CardDefaults.ContentPadding.calculateBottomPadding() +
+                CardDefaults.ContentPadding.calculateTopPadding(),
+            imageModifier = Modifier.requiredHeight(100.dp)
+        )
+
+    @Test
+    public fun gives_enabled_default_colors(): Unit =
+        verifyColors(
+            CardStatus.Enabled,
+        ) { MaterialTheme.colorScheme.onSurfaceVariant }
+
+    @Test
+    public fun gives_disabled_default_colors(): Unit =
+        verifyColors(
+            CardStatus.Disabled,
+        ) { MaterialTheme.colorScheme.onSurfaceVariant }
+
+    @Test
+    public fun app_card_gives_default_colors() {
+        var expectedAppColor = Color.Transparent
+        var expectedTimeColor = Color.Transparent
+        var expectedTitleColor = Color.Transparent
+        var expectedContentColor = Color.Transparent
+        var actualContentColor = Color.Transparent
+        var actualTitleColor = Color.Transparent
+        var actualTimeColor = Color.Transparent
+        var actualAppColor = Color.Transparent
+        val testBackground = Color.White
+
+        rule.setContentWithTheme {
+            expectedAppColor = MaterialTheme.colorScheme.onSurfaceVariant
+            expectedTimeColor = MaterialTheme.colorScheme.onSurfaceVariant
+            expectedTitleColor = MaterialTheme.colorScheme.onSurface
+            expectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(testBackground)
+            ) {
+                AppCard(
+                    onClick = {},
+                    appName = { actualAppColor = LocalContentColor.current },
+                    time = { actualTimeColor = LocalContentColor.current },
+                    title = { actualTitleColor = LocalContentColor.current },
+                    modifier = Modifier.testTag(TEST_TAG)
+                ) {
+                    actualContentColor = LocalContentColor.current
+                }
+            }
+        }
+
+        assertEquals(expectedAppColor, actualAppColor)
+        assertEquals(expectedTimeColor, actualTimeColor)
+        assertEquals(expectedTitleColor, actualTitleColor)
+        assertEquals(expectedContentColor, actualContentColor)
+    }
+
+    @Test
+    public fun title_card_gives_default_colors() {
+        var expectedTimeColor = Color.Transparent
+        var expectedTitleColor = Color.Transparent
+        var expectedContentColor = Color.Transparent
+        var actualContentColor = Color.Transparent
+        var actualTitleColor = Color.Transparent
+        var actualTimeColor = Color.Transparent
+        val testBackground = Color.White
+
+        rule.setContentWithTheme {
+            expectedTimeColor = MaterialTheme.colorScheme.onSurfaceVariant
+            expectedTitleColor = MaterialTheme.colorScheme.onSurface
+            expectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(testBackground)
+            ) {
+                TitleCard(
+                    onClick = {},
+                    time = { actualTimeColor = LocalContentColor.current },
+                    title = { actualTitleColor = LocalContentColor.current },
+                    modifier = Modifier.testTag(TEST_TAG)
+                ) {
+                    actualContentColor = LocalContentColor.current
+                }
+            }
+        }
+
+        assertEquals(expectedTimeColor, actualTimeColor)
+        assertEquals(expectedTitleColor, actualTitleColor)
+        assertEquals(expectedContentColor, actualContentColor)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    public fun outlined_card_has_outlined_border_and_transparent() {
+        val outlineColor = Color.Red
+        val testBackground = Color.Green
+
+        rule.setContentWithTheme {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(testBackground)
+            ) {
+                OutlinedCard(
+                    onClick = {},
+                    border = CardDefaults.outlinedCardBorder(outlineColor),
+                    modifier = Modifier
+                        .testTag(TEST_TAG)
+                        .size(100.dp)
+                        .align(Alignment.Center)
+                ) {
+                }
+            }
+        }
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(outlineColor)
+        // As the color of the OutlinedCard is transparent, we expect to see a
+        // testBackground color covering everything except border.
+        rule.onNodeWithTag(TEST_TAG).captureToImage()
+            .assertColorInPercentageRange(testBackground, 93f..97f)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    public fun outlined_titlecard_has_outlined_border_and_transparent() {
+        val outlineColor = Color.Red
+        val testBackground = Color.Green
+
+        rule.setContentWithTheme {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(testBackground)
+            ) {
+                TitleCard(
+                    onClick = {},
+                    title = {},
+                    border = CardDefaults.outlinedCardBorder(outlineColor),
+                    colors = CardDefaults.outlinedCardColors(),
+                    modifier = Modifier
+                        .testTag(TEST_TAG)
+                        .size(100.dp)
+                        .align(Alignment.Center)
+                ) {
+                }
+            }
+        }
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(outlineColor)
+        // As the color of the OutlinedCard is transparent, we expect to see a
+        // testBackground color covering everything except border.
+        rule.onNodeWithTag(TEST_TAG).captureToImage()
+            .assertColorInPercentageRange(testBackground, 93f..97f)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    public fun outlined_appcard_has_outlined_border_and_transparent() {
+        val outlineColor = Color.Red
+        val testBackground = Color.Green
+
+        rule.setContentWithTheme {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(testBackground)
+            ) {
+                AppCard(
+                    onClick = {},
+                    appName = {},
+                    title = {},
+                    border = CardDefaults.outlinedCardBorder(outlineColor),
+                    colors = CardDefaults.outlinedCardColors(),
+                    modifier = Modifier
+                        .testTag(TEST_TAG)
+                        .size(100.dp)
+                        .align(Alignment.Center)
+                ) {
+                }
+            }
+        }
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(outlineColor)
+        // As the color of the OutlinedCard is transparent, we expect to see a
+        // testBackground color covering everything except border.
+        rule.onNodeWithTag(TEST_TAG).captureToImage()
+            .assertColorInPercentageRange(testBackground, 93f..97f)
+    }
+
+    @Test
+    public fun gives_correct_text_style_base() {
+        var actualTextStyle = TextStyle.Default
+        var expectedTextStyle = TextStyle.Default
+        rule.setContentWithTheme {
+            expectedTextStyle = MaterialTheme.typography.bodyLarge
+            Card(
+                onClick = {},
+                content = {
+                    actualTextStyle = LocalTextStyle.current
+                },
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        assertEquals(expectedTextStyle, actualTextStyle)
+    }
+
+    @Test
+    public fun app_card_gives_correct_text_style_base() {
+        var actualAppTextStyle = TextStyle.Default
+        var actualTimeTextStyle = TextStyle.Default
+        var actualTitleTextStyle = TextStyle.Default
+        var actuaContentTextStyle = TextStyle.Default
+        var expectedAppTextStyle = TextStyle.Default
+        var expectedTimeTextStyle = TextStyle.Default
+        var expectedTitleTextStyle = TextStyle.Default
+        var expectedContentTextStyle = TextStyle.Default
+
+        rule.setContentWithTheme {
+            expectedAppTextStyle = MaterialTheme.typography.captionLarge
+            expectedTimeTextStyle = MaterialTheme.typography.captionLarge
+            expectedTitleTextStyle = MaterialTheme.typography.titleSmall
+            expectedContentTextStyle = MaterialTheme.typography.bodyLarge
+
+            AppCard(
+                onClick = {},
+                appName = {
+                    actualAppTextStyle = LocalTextStyle.current
+                },
+                time = {
+                    actualTimeTextStyle = LocalTextStyle.current
+                },
+                title = {
+                    actualTitleTextStyle = LocalTextStyle.current
+                },
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                actuaContentTextStyle = LocalTextStyle.current
+            }
+        }
+        assertEquals(expectedAppTextStyle, actualAppTextStyle)
+        assertEquals(expectedTimeTextStyle, actualTimeTextStyle)
+        assertEquals(expectedTitleTextStyle, actualTitleTextStyle)
+        assertEquals(expectedContentTextStyle, actuaContentTextStyle)
+    }
+
+    @Test
+    public fun title_card_gives_correct_text_style_base() {
+        var actualTimeTextStyle = TextStyle.Default
+        var actualTitleTextStyle = TextStyle.Default
+        var actuaContentTextStyle = TextStyle.Default
+        var expectedTimeTextStyle = TextStyle.Default
+        var expectedTitleTextStyle = TextStyle.Default
+        var expectedContentTextStyle = TextStyle.Default
+
+        rule.setContentWithTheme {
+            expectedTimeTextStyle = MaterialTheme.typography.captionLarge
+            expectedTitleTextStyle = MaterialTheme.typography.titleSmall
+            expectedContentTextStyle = MaterialTheme.typography.bodyLarge
+
+            TitleCard(
+                onClick = {},
+                time = {
+                    actualTimeTextStyle = LocalTextStyle.current
+                },
+                title = {
+                    actualTitleTextStyle = LocalTextStyle.current
+                },
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                actuaContentTextStyle = LocalTextStyle.current
+            }
+        }
+        assertEquals(expectedTimeTextStyle, actualTimeTextStyle)
+        assertEquals(expectedTitleTextStyle, actualTitleTextStyle)
+        assertEquals(expectedContentTextStyle, actuaContentTextStyle)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    public fun outlined_app_card_gives_correct_text_style_base() {
+        var actualAppTextStyle = TextStyle.Default
+        var actualTimeTextStyle = TextStyle.Default
+        var actualTitleTextStyle = TextStyle.Default
+        var actuaContentTextStyle = TextStyle.Default
+        var expectedAppTextStyle = TextStyle.Default
+        var expectedTimeTextStyle = TextStyle.Default
+        var expectedTitleTextStyle = TextStyle.Default
+        var expectedContentTextStyle = TextStyle.Default
+
+        rule.setContentWithTheme {
+            expectedAppTextStyle = MaterialTheme.typography.captionLarge
+            expectedTimeTextStyle = MaterialTheme.typography.captionLarge
+            expectedTitleTextStyle = MaterialTheme.typography.titleSmall
+            expectedContentTextStyle = MaterialTheme.typography.bodyLarge
+
+            AppCard(
+                onClick = {},
+                appName = {
+                    actualAppTextStyle = LocalTextStyle.current
+                },
+                time = {
+                    actualTimeTextStyle = LocalTextStyle.current
+                },
+                title = {
+                    actualTitleTextStyle = LocalTextStyle.current
+                },
+                modifier = Modifier.testTag(TEST_TAG)
+            ) {
+                actuaContentTextStyle = LocalTextStyle.current
+            }
+        }
+        rule.onNodeWithTag(TEST_TAG).captureToImage()
+        assertEquals(expectedAppTextStyle, actualAppTextStyle)
+        assertEquals(expectedTimeTextStyle, actualTimeTextStyle)
+        assertEquals(expectedTitleTextStyle, actualTitleTextStyle)
+        assertEquals(expectedContentTextStyle, actuaContentTextStyle)
+    }
+
+    private fun verifyHeight(expectedHeight: Dp, imageModifier: Modifier = Modifier) {
+        rule.verifyHeight(expectedHeight) {
+            Card(
+                onClick = {},
+            ) {
+                TestIcon(modifier = imageModifier)
+            }
+        }
+    }
+
+    private fun verifyColors(
+        status: CardStatus,
+        contentColor: @Composable () -> Color
+    ) {
+        var expectedContent = Color.Transparent
+        var actualContent = Color.Transparent
+        val testBackground = Color.White
+
+        rule.setContentWithTheme {
+            expectedContent = contentColor()
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(testBackground)
+            ) {
+                Card(
+                    onClick = {},
+                    content = { actualContent = LocalContentColor.current },
+                    enabled = status.enabled(),
+                    modifier = Modifier.testTag(TEST_TAG)
+                )
+            }
+        }
+
+        assertEquals(expectedContent, actualContent)
+    }
+}
+
+private fun ComposeContentTestRule.verifyHeight(expected: Dp, content: @Composable () -> Unit) {
+    setContentWithThemeForSizeAssertions {
+        content()
+    }.assertHeightIsEqualTo(expected, Dp(1.0f))
+}
+
+private enum class CardStatus {
+    Enabled,
+    Disabled;
+
+    fun enabled() = this == Enabled
+}
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
index 750e25e..2c8d474 100644
--- a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
@@ -34,19 +34,31 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.graphics.toPixelMap
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.assertTouchHeightIsEqualTo
 import androidx.compose.ui.test.assertTouchWidthIsEqualTo
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.ComposeContentTestRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpRect
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.height
+import androidx.compose.ui.unit.isUnspecified
+import androidx.compose.ui.unit.toSize
 import org.junit.Assert
+import kotlin.math.abs
 
+/**
+ * Constant to emulate very big but finite constraints
+ */
 val BigTestMaxWidth = 5000.dp
 val BigTestMaxHeight = 5000.dp
 
@@ -95,20 +107,17 @@
 ): SemanticsNodeInteraction {
     setContent {
         MaterialTheme {
-            Box {
-                Box(
-                    Modifier
-                        .sizeIn(
-                            maxWidth = parentMaxWidth,
-                            maxHeight = parentMaxHeight
-                        )
-                        .testTag("containerForSizeAssertion")
-                ) {
-                    content()
-                }
+            Box(
+                Modifier.sizeIn(
+                    maxWidth = parentMaxWidth,
+                    maxHeight = parentMaxHeight
+                ).testTag("containerForSizeAssertion")
+            ) {
+                content()
             }
         }
     }
+
     return onNodeWithTag("containerForSizeAssertion", useUnmergedTree)
 }
 
@@ -176,8 +185,115 @@
         .assertContainsColor(finalExpectedContainerColor)
 }
 
+/**
+ * Checks that [expectedColor]  is in the percentage [range] of an [ImageBitmap] color histogram
+ */
+fun ImageBitmap.assertColorInPercentageRange(
+    expectedColor: Color,
+    range: ClosedFloatingPointRange<Float> = 50.0f..100.0f
+) {
+    val histogram = histogram()
+    if (!histogram.containsKey(expectedColor)) {
+        throw AssertionError("Expected color $expectedColor was not found in the bitmap.")
+    }
+
+    ((histogram[expectedColor]!! * 100f) / (width * height)).let { actualPercent ->
+        if (actualPercent !in range) {
+            throw AssertionError(
+                "Expected color $expectedColor found " +
+                    "$actualPercent%, not in the percentage range $range"
+            )
+        }
+    }
+}
+
+/**
+ * Asserts that the layout of this node has height equal to [expectedHeight].
+ *
+ * @throws AssertionError if comparison fails.
+ */
+internal fun SemanticsNodeInteraction.assertHeightIsEqualTo(
+    expectedHeight: Dp,
+    tolerance: Dp = Dp(0.5f)
+): SemanticsNodeInteraction {
+    return withUnclippedBoundsInRoot {
+        it.height.assertIsEqualTo(expectedHeight, "height", tolerance)
+    }
+}
+
+private fun SemanticsNodeInteraction.withUnclippedBoundsInRoot(
+    assertion: (DpRect) -> Unit
+): SemanticsNodeInteraction {
+    val node = fetchSemanticsNode("Failed to retrieve bounds of the node.")
+    val bounds = with(node.root!!.density) {
+        node.unclippedBoundsInRoot.let {
+            DpRect(it.left.toDp(), it.top.toDp(), it.right.toDp(), it.bottom.toDp())
+        }
+    }
+    assertion.invoke(bounds)
+    return this
+}
+
+private val SemanticsNode.unclippedBoundsInRoot: Rect
+    get() {
+        return if (layoutInfo.isPlaced) {
+            Rect(positionInRoot, size.toSize())
+        } else {
+            Dp.Unspecified.value.let { Rect(it, it, it, it) }
+        }
+    }
+
+/**
+ * Returns if this value is equal to the [reference], within a given [tolerance]. If the
+ * reference value is [Float.NaN], [Float.POSITIVE_INFINITY] or [Float.NEGATIVE_INFINITY], this
+ * only returns true if this value is exactly the same (tolerance is disregarded).
+ */
+private fun Dp.isWithinTolerance(reference: Dp, tolerance: Dp): Boolean {
+    return when {
+        reference.isUnspecified -> this.isUnspecified
+        reference.value.isInfinite() -> this.value == reference.value
+        else -> abs(this.value - reference.value) <= tolerance.value
+    }
+}
+
+/**
+ * Asserts that this value is equal to the given [expected] value.
+ *
+ * Performs the comparison with the given [tolerance] or the default one if none is provided. It is
+ * recommended to use tolerance when comparing positions and size coming from the framework as there
+ * can be rounding operation performed by individual layouts so the values can be slightly off from
+ * the expected ones.
+ *
+ * @param expected The expected value to which this one should be equal to.
+ * @param subject Used in the error message to identify which item this assertion failed on.
+ * @param tolerance The tolerance within which the values should be treated as equal.
+ *
+ * @throws AssertionError if comparison fails.
+ */
+internal fun Dp.assertIsEqualTo(expected: Dp, subject: String, tolerance: Dp = Dp(.5f)) {
+    if (!isWithinTolerance(expected, tolerance)) {
+        // Comparison failed, report the error in DPs
+        throw AssertionError(
+            "Actual $subject is $this, expected $expected (tolerance: $tolerance)"
+        )
+    }
+}
+
+private fun ImageBitmap.histogram(): MutableMap<Color, Long> {
+    val pixels = this.toPixelMap()
+    val histogram = mutableMapOf<Color, Long>()
+    for (x in 0 until width) {
+        for (y in 0 until height) {
+            val color = pixels[x, y]
+            histogram[color] = histogram.getOrDefault(color, 0) + 1
+        }
+    }
+    return histogram
+}
+
 internal enum class Status {
     Enabled,
     Disabled;
+
     fun enabled() = this == Enabled
 }
diff --git a/wear/compose/compose-material3/src/androidMain/AndroidManifest.xml b/wear/compose/compose-material3/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/compose/compose-material3/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Button.kt
index fa6786f..7503da4 100644
--- a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Button.kt
@@ -77,8 +77,6 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
  */
 @Composable
 fun Button(
@@ -90,7 +88,6 @@
     border: BorderStroke? = null,
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
     content: @Composable RowScope.() -> Unit,
 ) {
     androidx.wear.compose.materialcore.Chip(
@@ -102,7 +99,7 @@
         contentPadding = contentPadding,
         shape = shape,
         interactionSource = interactionSource,
-        role = role,
+        role = Role.Button,
         content = provideScopeContent(
             colors.contentColor(enabled = enabled),
             MaterialTheme.typography.buttonMedium,
@@ -151,8 +148,6 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
  */
 @Composable
 fun FilledTonalButton(
@@ -164,11 +159,9 @@
     border: BorderStroke? = null,
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
     content: @Composable RowScope.() -> Unit,
 ) = Button(
-    onClick, modifier, enabled, shape, colors, border, contentPadding, interactionSource, role,
-    content
+    onClick, modifier, enabled, shape, colors, border, contentPadding, interactionSource, content
 )
 
 /**
@@ -210,8 +203,6 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
  */
 @Composable
 fun OutlinedButton(
@@ -223,11 +214,9 @@
     border: BorderStroke? = ButtonDefaults.outlinedButtonBorder(enabled),
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
     content: @Composable RowScope.() -> Unit,
 ) = Button(
-    onClick, modifier, enabled, shape, colors, border, contentPadding, interactionSource, role,
-    content
+    onClick, modifier, enabled, shape, colors, border, contentPadding, interactionSource, content
 )
 
 /**
@@ -269,8 +258,6 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
  */
 @Composable
 fun ChildButton(
@@ -282,11 +269,9 @@
     border: BorderStroke? = null,
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
     content: @Composable RowScope.() -> Unit,
 ) = Button(
-    onClick, modifier, enabled, shape, colors, border, contentPadding, interactionSource, role,
-    content
+    onClick, modifier, enabled, shape, colors, border, contentPadding, interactionSource, content
 )
 
 /**
@@ -316,8 +301,6 @@
  *
  * TODO(b/261838497) Add Material3 samples and UX guidance links
  *
- * @param label A slot for providing the button's main label. The contents are expected to be text
- * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  * @param onClick Will be called when the user clicks the button
  * @param modifier Modifier to be applied to the button
  * @param secondaryLabel A slot for providing the button's secondary label. The contents are
@@ -343,12 +326,11 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
+ * @param label A slot for providing the button's main label. The contents are expected to be text
+ * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  */
 @Composable
 fun Button(
-    label: @Composable RowScope.() -> Unit,
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     secondaryLabel: (@Composable RowScope.() -> Unit)? = null,
@@ -359,7 +341,7 @@
     border: BorderStroke? = null,
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
+    label: @Composable RowScope.() -> Unit,
 ) {
     androidx.wear.compose.materialcore.Chip(
         modifier = modifier.height(ButtonDefaults.Height),
@@ -384,7 +366,7 @@
             provideScopeContent(colors.iconColor(enabled), icon)
         },
         defaultIconSpacing = ButtonDefaults.IconSpacing,
-        role = role
+        role = Role.Button
     )
 }
 
@@ -418,8 +400,6 @@
  *
  * TODO(b/261838497) Add Material3 samples and UX guidance links
  *
- * @param label A slot for providing the button's main label. The contents are expected to be text
- * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  * @param onClick Will be called when the user clicks the button
  * @param modifier Modifier to be applied to the button
  * @param secondaryLabel A slot for providing the button's secondary label. The contents are
@@ -444,12 +424,11 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
+ * @param label A slot for providing the button's main label. The contents are expected to be text
+ * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  */
 @Composable
 fun FilledTonalButton(
-    label: @Composable RowScope.() -> Unit,
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     secondaryLabel: (@Composable RowScope.() -> Unit)? = null,
@@ -460,9 +439,8 @@
     border: BorderStroke? = null,
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
+    label: @Composable RowScope.() -> Unit,
 ) = Button(
-    label,
     onClick,
     modifier,
     secondaryLabel,
@@ -473,7 +451,7 @@
     border,
     contentPadding,
     interactionSource,
-    role
+    label
 )
 
 /**
@@ -503,8 +481,6 @@
  *
  * TODO(b/261838497) Add Material3 samples and UX guidance links
  *
- * @param label A slot for providing the button's main label. The contents are expected to be text
- * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  * @param onClick Will be called when the user clicks the button
  * @param modifier Modifier to be applied to the button
  * @param secondaryLabel A slot for providing the button's secondary label. The contents are
@@ -529,12 +505,11 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
+ * @param label A slot for providing the button's main label. The contents are expected to be text
+ * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  */
 @Composable
 fun OutlinedButton(
-    label: @Composable RowScope.() -> Unit,
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     secondaryLabel: (@Composable RowScope.() -> Unit)? = null,
@@ -545,9 +520,8 @@
     border: BorderStroke? = ButtonDefaults.outlinedButtonBorder(enabled),
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
+    label: @Composable RowScope.() -> Unit,
 ) = Button(
-    label,
     onClick,
     modifier,
     secondaryLabel,
@@ -558,7 +532,7 @@
     border,
     contentPadding,
     interactionSource,
-    role
+    label,
 )
 
 /**
@@ -587,8 +561,6 @@
  *
  * TODO(b/261838497) Add Material3 samples and UX guidance links
  *
- * @param label A slot for providing the button's main label. The contents are expected to be text
- * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  * @param onClick Will be called when the user clicks the button
  * @param modifier Modifier to be applied to the button
  * @param secondaryLabel A slot for providing the button's secondary label. The contents are
@@ -613,12 +585,11 @@
  * [Interaction]s for this Button. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
- * @param role Role semantics that accessibility services can use to provide more
- * context to users.
+ * @param label A slot for providing the button's main label. The contents are expected to be text
+ * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  */
 @Composable
 fun ChildButton(
-    label: @Composable RowScope.() -> Unit,
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     secondaryLabel: (@Composable RowScope.() -> Unit)? = null,
@@ -629,9 +600,8 @@
     border: BorderStroke? = null,
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    role: Role? = Role.Button,
+    label: @Composable RowScope.() -> Unit,
 ) = Button(
-    label,
     onClick,
     modifier,
     secondaryLabel,
@@ -642,7 +612,7 @@
     border,
     contentPadding,
     interactionSource,
-    role
+    label,
 )
 
 /**
@@ -924,7 +894,7 @@
  * in different states.
  */
 @Immutable
-class ButtonColors internal constructor(
+class ButtonColors constructor(
     private val containerPainter: Painter,
     private val contentColor: Color,
     private val secondaryContentColor: Color,
@@ -946,7 +916,7 @@
      * @param disabledSecondaryContentColor The content color of this [Button] when not enabled
      * @param disabledIconColor The content color of this [Button] when not enabled
      */
-    internal constructor(
+    constructor(
         containerColor: Color,
         contentColor: Color,
         secondaryContentColor: Color,
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Card.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Card.kt
new file mode 100644
index 0000000..5454a04
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Card.kt
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.painter.ColorPainter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.materialcore.ImageWithScrimPainter
+import androidx.wear.compose.materialcore.Text
+
+/**
+ * Base level Wear Material 3 [Card] that offers a single slot to take any content.
+ *
+ * Is used as the container for more opinionated [Card] components that take specific content such
+ * as icons, images, titles, subtitles and labels.
+ *
+ * The [Card] is Rectangle shaped rounded corners by default.
+ *
+ * Cards can be enabled or disabled. A disabled card will not respond to click events.
+ *
+ * For more information, see the
+ * [Cards](https://developer.android.com/training/wearables/components/cards)
+ * Wear OS Material design guide.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param modifier Modifier to be applied to the card
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param colors [CardColors] that will be used to resolve the colors used for this card in
+ * different states. See [CardDefaults.cardColors].
+ * @param border A BorderStroke object which is used for drawing outlines.
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param content The main slot for a content of this card
+ */
+@Composable
+fun Card(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = MaterialTheme.shapes.large,
+    colors: CardColors = CardDefaults.cardColors(),
+    border: BorderStroke? = null,
+    contentPadding: PaddingValues = CardDefaults.ContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    androidx.wear.compose.materialcore.Card(
+        onClick = onClick,
+        modifier = modifier,
+        border = border,
+        containerPainter = colors.containerPainter,
+        enabled = enabled,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        role = null,
+        shape = shape,
+    ) {
+        CompositionLocalProvider(
+            LocalContentColor provides colors.contentColor,
+            LocalTextStyle provides MaterialTheme.typography.bodyLarge,
+        ) {
+            content()
+        }
+    }
+}
+
+/**
+ * Opinionated Wear Material 3 [Card] that offers a specific 5 slot layout to show information about
+ * an application, e.g. a notification. AppCards are designed to show interactive elements from
+ * multiple applications. They will typically be used by the system UI, e.g. for showing a list of
+ * notifications from different applications. However it could also be adapted by individual
+ * application developers to show information about different parts of their application.
+ *
+ * The first row of the layout has three slots, 1) a small optional application [Image] or [Icon] of
+ * size [CardDefaults.AppImageSize]x[CardDefaults.AppImageSize] dp, 2) an application name
+ * (emphasised with the [CardColors.appColor()] color), it is expected to be a short start aligned
+ * [Text] composable, and 3) the time that the application activity has occurred which will be
+ * shown on the top row of the card, this is expected to be an end aligned [Text] composable
+ * showing a time relevant to the contents of the [Card].
+ *
+ * The second row shows a title, this is expected to be a single row of start aligned [Text].
+ *
+ * The rest of the [Card] contains the content which can be either [Text] or an [Image].
+ * If the content is text it can be single or multiple line and is expected to be Top and Start
+ * aligned.
+ *
+ * If more than one composable is provided in the content slot it is the responsibility of the
+ * caller to determine how to layout the contents, e.g. provide either a row or a column.
+ *
+ * For more information, see the
+ * [Cards](https://developer.android.com/training/wearables/components/cards)
+ * guide.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param appName A slot for displaying the application name, expected to be a single line of start
+ * aligned text of [Typography.captionLarge]
+ * @param title A slot for displaying the title of the card, expected to be one or two lines of
+ * start aligned text of [Typography.titleSmall]
+ * @param modifier Modifier to be applied to the card
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param colors [CardColors] that will be used to resolve the colors used for this card in
+ * different states. See [CardDefaults.cardColors].
+ * @param border A BorderStroke object which is used for drawing outlines.
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param appImage A slot for a small ([CardDefaults.AppImageSize]x[CardDefaults.AppImageSize] )
+ * [Image] associated with the application.
+ * @param time A slot for displaying the time relevant to the contents of the card, expected to be a
+ * short piece of end aligned text of [Typography.captionLarge].
+ * @param content The main slot for a content of this card
+ */
+@Composable
+fun AppCard(
+    onClick: () -> Unit,
+    appName: @Composable RowScope.() -> Unit,
+    title: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = MaterialTheme.shapes.large,
+    colors: CardColors = CardDefaults.cardColors(),
+    border: BorderStroke? = null,
+    contentPadding: PaddingValues = CardDefaults.ContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    appImage: @Composable (RowScope.() -> Unit)? = null,
+    time: @Composable (RowScope.() -> Unit)? = null,
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    androidx.wear.compose.materialcore.AppCard(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        shape = shape,
+        containerPainter = colors.containerPainter,
+        border = border,
+        contentPadding = contentPadding,
+        appImage = appImage?.let { { appImage() } },
+        interactionSource = interactionSource,
+        appName = {
+            CompositionLocalProvider(
+                LocalContentColor provides colors.appNameColor,
+                LocalTextStyle provides MaterialTheme.typography.captionLarge,
+            ) {
+                appName()
+            }
+        },
+        time = time?.let {
+            {
+                CompositionLocalProvider(
+                    LocalContentColor provides colors.timeColor,
+                    LocalTextStyle provides MaterialTheme.typography.captionLarge,
+                ) {
+                    time()
+                }
+            }
+        },
+        title = {
+            CompositionLocalProvider(
+                LocalContentColor provides colors.titleColor,
+                LocalTextStyle provides MaterialTheme.typography.titleSmall,
+            ) {
+                title()
+            }
+        },
+        content = {
+            CompositionLocalProvider(
+                LocalContentColor provides colors.contentColor,
+                LocalTextStyle provides MaterialTheme.typography.bodyLarge,
+            ) {
+                content()
+            }
+        }
+    )
+}
+
+/**
+ * Opinionated Wear Material 3 [Card] that offers a specific 3 slot layout to show interactive
+ * information about an application, e.g. a message. TitleCards are designed for use within an
+ * application.
+ *
+ * The first row of the layout has two slots. 1. a start aligned title. The title text is
+ * expected to be a maximum of 2 lines of text.
+ * 2. An optional time that the application activity has occurred shown at the
+ * end of the row, expected to be an end aligned [Text] composable showing a time relevant to the
+ * contents of the [Card].
+ *
+ * The rest of the [Card] contains the content which is expected to be [Text] or a contained
+ * [Image].
+ *
+ * If the content is text it can be single or multiple line and is expected to be Top and Start
+ * aligned and of type of [Typography.bodyMedium].
+ *
+ * Overall the [title] and [content] text should be no more than 5 rows of text combined.
+ *
+ * If more than one composable is provided in the content slot it is the responsibility of the
+ * caller to determine how to layout the contents, e.g. provide either a row or a column.
+ *
+ * For more information, see the
+ * [Cards](https://developer.android.com/training/wearables/components/cards)
+ * guide.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param title A slot for displaying the title of the card, expected to be one or two lines of text
+ * of [Typography.buttonMedium]
+ * @param modifier Modifier to be applied to the card
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param colors [CardColors] that will be used to resolve the colors used for this card in
+ * different states. See [CardDefaults.cardColors].
+ * @param border A BorderStroke object which is used for drawing outlines.
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param time An optional slot for displaying the time relevant to the contents of the card,
+ * expected to be a short piece of end aligned text.
+ * @param content The main slot for a content of this card
+ */
+@Composable
+fun TitleCard(
+    onClick: () -> Unit,
+    title: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = MaterialTheme.shapes.large,
+    colors: CardColors = CardDefaults.cardColors(),
+    border: BorderStroke? = null,
+    contentPadding: PaddingValues = CardDefaults.ContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    time: @Composable (RowScope.() -> Unit)? = null,
+    content: @Composable () -> Unit,
+) {
+    androidx.wear.compose.materialcore.TitleCard(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        shape = shape,
+        border = border,
+        contentPadding = contentPadding,
+        containerPainter = colors.containerPainter,
+        interactionSource = interactionSource,
+        title = {
+            CompositionLocalProvider(
+                LocalContentColor provides colors.titleColor,
+                LocalTextStyle provides MaterialTheme.typography.titleSmall,
+            ) {
+                title()
+            }
+        },
+        time = {
+            time?.let {
+                Spacer(modifier = Modifier.weight(1.0f))
+                CompositionLocalProvider(
+                    LocalContentColor provides colors.timeColor,
+                    LocalTextStyle provides MaterialTheme.typography.captionLarge,
+                ) {
+                    time()
+                }
+            }
+        },
+        content = {
+            CompositionLocalProvider(
+                values = arrayOf(
+                    LocalContentColor provides colors.contentColor,
+                    LocalTextStyle provides MaterialTheme.typography.bodyLarge
+                ),
+                content = content
+            )
+        }
+    )
+}
+
+/**
+ * Outlined Wear Material 3 [Card] that offers a single slot to take any content.
+ *
+ * Outlined [Card] components that take specific content such
+ * as icons, images, titles, subtitles and labels. Outlined Cards have a
+ * visual boundary around the container. This can emphasise the content of this card.
+ *
+ * The [Card] is Rectangle shaped with rounded corners by default.
+ *
+ * Cards can be enabled or disabled. A disabled card will not respond to click events.
+ *
+ * For more information, see the
+ * [Cards](https://developer.android.com/training/wearables/components/cards)
+ * Wear OS Material design guide.
+ *
+ * @param onClick Will be called when the user clicks the card
+ * @param modifier Modifier to be applied to the card
+ * @param enabled Controls the enabled state of the card. When false, this card will not
+ * be clickable and there will be no ripple effect on click. Wear cards do not have any specific
+ * elevation or alpha differences when not enabled - they are simply not clickable.
+ * @param shape Defines the card's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param colors [CardColors] that will be used to resolve the colors used for this card in
+ * different states. See [CardDefaults.cardColors].
+ * @param border A BorderStroke object which is used for the outline drawing.
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this card. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this card in different [Interaction]s.
+ * @param content The main slot for a content of this card
+ */
+@Composable
+fun OutlinedCard(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = MaterialTheme.shapes.large,
+    colors: CardColors = CardDefaults.outlinedCardColors(),
+    border: BorderStroke = CardDefaults.outlinedCardBorder(),
+    contentPadding: PaddingValues = CardDefaults.ContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    Card(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        colors = colors,
+        border = border,
+        interactionSource = interactionSource,
+        contentPadding = contentPadding,
+        shape = shape,
+        content = content
+    )
+}
+
+/**
+ * Contains the default values used by [Card]
+ */
+public object CardDefaults {
+
+    /**
+     * Creates a [CardColors] that represents the default container and content colors used in a
+     * [Card], [AppCard] or [TitleCard].
+     *
+     * @param containerColor the container color of this [Card].
+     * @param contentColor the content color of this [Card].
+     * @param appNameColor the color used for appName, only applies to [AppCard].
+     * @param timeColor the color used for time, applies to [AppCard] and [TitleCard].
+     * @param titleColor the color used for title, applies to [AppCard] and [TitleCard].
+     */
+    @Composable
+    public fun cardColors(
+        containerColor: Color = MaterialTheme.colorScheme.surface,
+        contentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        appNameColor: Color = contentColor,
+        timeColor: Color = contentColor,
+        titleColor: Color = MaterialTheme.colorScheme.onSurface,
+    ): CardColors = CardColors(
+        containerPainter = remember(containerColor) { ColorPainter(containerColor) },
+        contentColor = contentColor,
+        appNameColor = appNameColor,
+        timeColor = timeColor,
+        titleColor = titleColor
+    )
+
+    /**
+     * Creates a [CardColors] that represents the default container and content colors used in an
+     * [OutlinedCard], outlined [AppCard] or [TitleCard].
+     *
+     * @param contentColor the content color of this [OutlinedCard].
+     * @param appNameColor the color used for appName, only applies to [AppCard].
+     * @param timeColor the color used for time, applies to [AppCard] and [TitleCard].
+     * @param titleColor the color used for title, applies to [AppCard] and [TitleCard].
+     */
+    @Composable
+    public fun outlinedCardColors(
+        contentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        appNameColor: Color = contentColor,
+        timeColor: Color = contentColor,
+        titleColor: Color = MaterialTheme.colorScheme.onSurface,
+    ): CardColors = CardColors(
+        containerPainter = remember { ColorPainter(Color.Transparent) },
+        contentColor = contentColor,
+        appNameColor = appNameColor,
+        timeColor = timeColor,
+        titleColor = titleColor
+    )
+
+    /**
+     * Creates a [CardColors] that represents the default container and content colors
+     * used in a [Card], [AppCard] or [TitleCard] with Image set as a background.
+     *
+     * @param containerPainter a Painter which is used for background drawing.
+     * @param contentColor the content color of this [Card].
+     * @param appNameColor the color used for appName, only applies to [AppCard].
+     * @param timeColor the color used for time, applies to [AppCard] and [TitleCard].
+     * @param titleColor the color used for title, applies to [AppCard] and [TitleCard].
+     */
+    @Composable
+    public fun imageCardColors(
+        containerPainter: Painter,
+        contentColor: Color = MaterialTheme.colorScheme.onBackground,
+        appNameColor: Color = contentColor,
+        timeColor: Color = contentColor,
+        titleColor: Color = contentColor,
+    ): CardColors = CardColors(
+        containerPainter = containerPainter,
+        contentColor = contentColor,
+        appNameColor = appNameColor,
+        timeColor = timeColor,
+        titleColor = titleColor
+    )
+
+    /**
+     * Creates a [Painter] for the background of a [Card] that displays an Image with a scrim over
+     * the image to make sure that any content above the background will be legible.
+     *
+     * An Image background is a means to reinforce the meaning of information in a Card, e.g. To
+     * help to contextualize the information in a TitleCard
+     *
+     * Cards should have a content color that contrasts with the background image and scrim
+     *
+     * @param backgroundImagePainter The [Painter] to use to draw the background of the [Card]
+     * @param backgroundImageScrimBrush The [Brush] to use to paint a scrim over the background
+     * image to ensure that any text drawn over the image is legible
+     */
+    @Composable
+    public fun imageWithScrimBackgroundPainter(
+        backgroundImagePainter: Painter,
+        backgroundImageScrimBrush: Brush = SolidColor(OverlayScrimColor)
+    ): Painter {
+        return ImageWithScrimPainter(
+            imagePainter = backgroundImagePainter,
+            brush = backgroundImageScrimBrush
+        )
+    }
+
+    /**
+     * Creates a [BorderStroke] that represents the default border used in Outlined Cards.
+     * @param outlineColor The color to be used for drawing an outline.
+     * @param borderWidth width of the border in [Dp].
+     */
+    @Composable
+    public fun outlinedCardBorder(
+        outlineColor: Color = MaterialTheme.colorScheme.outline,
+        borderWidth: Dp = 1.dp
+    ): BorderStroke =
+        BorderStroke(borderWidth, outlineColor)
+
+    private val CardHorizontalPadding = 10.dp
+    private val CardVerticalPadding = 10.dp
+
+    private val OverlayScrimColor: Color = Color(0x99202124)
+
+    /**
+     * The default content padding used by [Card]
+     */
+    public val ContentPadding: PaddingValues = PaddingValues(
+        start = CardHorizontalPadding,
+        top = CardVerticalPadding,
+        end = CardHorizontalPadding,
+        bottom = CardVerticalPadding
+    )
+
+    /**
+     * The default size of the app icon/image when used inside a [AppCard].
+     */
+    public val AppImageSize: Dp = 16.dp
+}
+
+/**
+ * Represents Colors used in [Card].
+ * Unlike other Material 3 components, Cards do not change their color appearance when
+ * they are disabled. All colors remain the same in enabled and disabled states
+ */
+@Immutable
+public class CardColors(
+    internal val containerPainter: Painter,
+    internal val contentColor: Color,
+    internal val appNameColor: Color,
+    internal val timeColor: Color,
+    internal val titleColor: Color,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is CardColors) return false
+
+        if (containerPainter != other.containerPainter) return false
+        if (contentColor != other.contentColor) return false
+        if (appNameColor != other.appNameColor) return false
+        if (timeColor != other.timeColor) return false
+        if (titleColor != other.titleColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerPainter.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        result = 31 * result + appNameColor.hashCode()
+        result = 31 * result + timeColor.hashCode()
+        result = 31 * result + titleColor.hashCode()
+        return result
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/IconButton.kt
index 087dd93..d4b2a43 100644
--- a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/IconButton.kt
@@ -423,7 +423,7 @@
  * [OutlinedIconButton].
  */
 @Immutable
-class IconButtonColors internal constructor(
+class IconButtonColors constructor(
     private val containerColor: Color,
     private val contentColor: Color,
     private val disabledContainerColor: Color,
diff --git a/wear/compose/compose-navigation/src/androidTest/AndroidManifest.xml b/wear/compose/compose-navigation/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 3c3c224..0000000
--- a/wear/compose/compose-navigation/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/wear/compose/compose-navigation/src/main/AndroidManifest.xml b/wear/compose/compose-navigation/src/main/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/compose/compose-navigation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/compose/integration-tests/demos/src/androidTest/AndroidManifest.xml b/wear/compose/integration-tests/demos/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e865f27..0000000
--- a/wear/compose/integration-tests/demos/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/HorizontalPageIndicatorDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/HorizontalPageIndicatorDemo.kt
index 24fa8b8..163277e 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/HorizontalPageIndicatorDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/HorizontalPageIndicatorDemo.kt
@@ -24,7 +24,7 @@
 import androidx.compose.foundation.shape.GenericShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -37,7 +37,7 @@
 @Composable
 fun CustomizedHorizontalPageIndicator() {
     val maxPages = 6
-    var selectedPage by remember { mutableStateOf(0) }
+    var selectedPage by remember { mutableIntStateOf(0) }
     val animatedSelectedPage by animateFloatAsState(
         targetValue = selectedPage.toFloat(),
         animationSpec = TweenSpec(durationMillis = 500)
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PositionIndicatorDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PositionIndicatorDemos.kt
index d8f3599..27633f6 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PositionIndicatorDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PositionIndicatorDemos.kt
@@ -32,6 +32,8 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -109,9 +111,9 @@
 
 @Composable
 fun ControllablePositionIndicator() {
-    val position = remember { mutableStateOf(0.2f) }
-    val size = remember { mutableStateOf(0.5f) }
-    var alignment by remember { mutableStateOf(0) }
+    val position = remember { mutableFloatStateOf(0.2f) }
+    val size = remember { mutableFloatStateOf(0.5f) }
+    var alignment by remember { mutableIntStateOf(0) }
     var reverseDirection by remember { mutableStateOf(false) }
     var layoutDirection by remember { mutableStateOf(false) }
     val actualLayoutDirection =
@@ -148,17 +150,17 @@
                     Text("Position")
                     DefaultInlineSlider(
                         modifier = Modifier.height(40.dp),
-                        value = position.value,
+                        value = position.floatValue,
                         valueRange = 0f..1f,
                         steps = 9,
-                        onValueChange = { position.value = it })
+                        onValueChange = { position.floatValue = it })
                     Text("Size")
                     DefaultInlineSlider(
                         modifier = Modifier.height(40.dp),
-                        value = size.value,
+                        value = size.floatValue,
                         valueRange = 0f..1f,
                         steps = 9,
-                        onValueChange = { size.value = it })
+                        onValueChange = { size.floatValue = it })
                     Row(modifier = Modifier.align(Alignment.CenterHorizontally)) {
                         Button(onClick = { alignment = (alignment + 1) % 3 }) {
                             Text(alignmentNames[alignment])
@@ -186,10 +188,10 @@
 @Composable
 fun SharedPositionIndicator() {
     val listStates = listOf(rememberScrollState(), rememberScrollState())
-    val selected = remember { mutableStateOf(0) }
+    val selected = remember { mutableIntStateOf(0) }
     Scaffold(
         positionIndicator = {
-            PositionIndicator(listStates[selected.value])
+            PositionIndicator(listStates[selected.intValue])
         }
     ) {
         Row(modifier = Modifier.fillMaxSize()) {
@@ -203,7 +205,7 @@
                 ) {
                     repeat(10) {
                         Chip(
-                            onClick = { selected.value = listIndex },
+                            onClick = { selected.intValue = listIndex },
                             label = { Text("#$it") }
                         )
                     }
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
index 93789b3..23056f3 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -50,9 +51,9 @@
 
 @Composable
 public fun ProgressWithCustomAngles() {
-    var startAngle by remember { mutableStateOf(292.5f) }
-    var endAngle by remember { mutableStateOf(247.5f) }
-    var progress by remember { mutableStateOf(0.5f) }
+    var startAngle by remember { mutableFloatStateOf(292.5f) }
+    var endAngle by remember { mutableFloatStateOf(247.5f) }
+    var progress by remember { mutableFloatStateOf(0.5f) }
     val animatedProgress: Float by animateFloatAsState(targetValue = progress)
 
     Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt
index df5330a..8e038f0 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt
@@ -23,6 +23,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -42,8 +44,8 @@
 
 @Composable
 fun InlineSliderDemo() {
-    var valueWithoutSegments by remember { mutableStateOf(5f) }
-    var valueWithSegments by remember { mutableStateOf(2f) }
+    var valueWithoutSegments by remember { mutableFloatStateOf(5f) }
+    var valueWithSegments by remember { mutableFloatStateOf(2f) }
     var enabled by remember { mutableStateOf(true) }
 
     ScalingLazyColumnWithRSB(
@@ -99,8 +101,8 @@
 
 @Composable
 fun InlineSliderWithIntegersDemo() {
-    var valueWithoutSegments by remember { mutableStateOf(5) }
-    var valueWithSegments by remember { mutableStateOf(2) }
+    var valueWithoutSegments by remember { mutableIntStateOf(5) }
+    var valueWithSegments by remember { mutableIntStateOf(2) }
 
     ScalingLazyColumnWithRSB(
         horizontalAlignment = Alignment.CenterHorizontally,
@@ -140,7 +142,7 @@
 
 @Composable
 fun InlineSliderCustomColorsDemo() {
-    var value by remember { mutableStateOf(4.5f) }
+    var value by remember { mutableFloatStateOf(4.5f) }
     Box(
         contentAlignment = Alignment.Center,
         modifier = Modifier.fillMaxSize()
@@ -161,8 +163,8 @@
 
 @Composable
 fun InlineSliderSegmented() {
-    var numberOfSegments by remember { mutableStateOf(5f) }
-    var progress by remember { mutableStateOf(10f) }
+    var numberOfSegments by remember { mutableFloatStateOf(5f) }
+    var progress by remember { mutableFloatStateOf(10f) }
 
     ScalingLazyColumnWithRSB(
         horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt
index 8b3db7e..3f6af0a 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt
@@ -19,7 +19,8 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -35,7 +36,7 @@
 
 @Composable
 fun StepperDemo() {
-    var value by remember { mutableStateOf(2f) }
+    var value by remember { mutableFloatStateOf(2f) }
     Stepper(
         value = value,
         onValueChange = { value = it },
@@ -48,7 +49,7 @@
 
 @Composable
 fun StepperWithIntegerDemo() {
-    var value by remember { mutableStateOf(2) }
+    var value by remember { mutableIntStateOf(2) }
     Stepper(
         value = value,
         onValueChange = { value = it },
@@ -60,26 +61,26 @@
 
 @Composable
 fun StepperWithScrollBarDemo() {
-    val valueState = remember { mutableStateOf(4f) }
+    var valueState by remember { mutableFloatStateOf(4f) }
     val range = 0f..10f
 
     Stepper(
-        value = valueState.value,
-        onValueChange = { valueState.value = it },
+        value = valueState,
+        onValueChange = { valueState = it },
         increaseIcon = { Icon(StepperDefaults.Increase, "Increase") },
         decreaseIcon = { Icon(StepperDefaults.Decrease, "Decrease") },
         valueRange = range,
         steps = 9
     ) {
         Chip(
-            onClick = { valueState.value = if (valueState.value == 0f) 4f else 0f },
+            onClick = { valueState = if (valueState == 0f) 4f else 0f },
             modifier = Modifier.width(146.dp),
             colors = ChipDefaults.secondaryChipColors(),
-            label = { Text("Volume : ${valueState.value}") },
+            label = { Text("Volume : $valueState") },
             icon = {
                 Icon(
                     painter = painterResource(
-                        id = if (valueState.value > 0)
+                        id = if (valueState > 0)
                             R.drawable.ic_volume_up_24px
                         else R.drawable.ic_volume_off_24px
                     ),
@@ -90,30 +91,30 @@
     }
 
     PositionIndicator(
-        value = { valueState.value },
+        value = { valueState },
         range = range
     )
 }
 
 @Composable
 fun StepperWithCustomColors() {
-    val valueState = remember { mutableStateOf(4f) }
+    var valueState by remember { mutableFloatStateOf(4f) }
     val range = 0f..10f
 
     Stepper(
-        value = valueState.value,
-        onValueChange = { valueState.value = it },
+        value = valueState,
+        onValueChange = { valueState = it },
         valueRange = range,
         increaseIcon = { Icon(StepperDefaults.Increase, "Increase") },
         decreaseIcon = { Icon(StepperDefaults.Decrease, "Decrease") },
         steps = 9,
         contentColor = AlternatePrimaryColor2,
     ) {
-        Text("Volume : ${valueState.value}")
+        Text("Volume : $valueState")
     }
 
     PositionIndicator(
-        value = { valueState.value },
+        value = { valueState },
         range = range
     )
 }
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemoWithState.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemoWithState.kt
index 56e01fb..1f4bef8 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemoWithState.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToDismissDemoWithState.kt
@@ -23,6 +23,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
@@ -66,7 +67,7 @@
         content = { isBackground ->
             if (showCounterForContent.value xor isBackground)
                 saveableStateHolder.SaveableStateProvider(counterKey) {
-                    var counter by rememberSaveable { mutableStateOf(0) }
+                    var counter by rememberSaveable { mutableIntStateOf(0) }
                     Column(
                         modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary),
                         horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
index 6d3441f..7c60533 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
+++ b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
@@ -37,6 +37,7 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -527,7 +528,7 @@
         horizontalAlignment = Alignment.CenterHorizontally
     ) {
         ListHeader { Text("Sliders") }
-        var value by remember { mutableStateOf(4.5f) }
+        var value by remember { mutableFloatStateOf(4.5f) }
         InlineSlider(
             value = value,
             onValueChange = { value = it },
@@ -552,7 +553,7 @@
 
 @Composable
 fun Stepper() {
-    var value by remember { mutableStateOf(2f) }
+    var value by remember { mutableFloatStateOf(2f) }
     Stepper(
         value = value,
         onValueChange = { value = it },
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/wear/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index e0788d6..0000000
--- a/wear/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/wear/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index f734545..0000000
--- a/wear/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
\ No newline at end of file
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
index db32f5c..e2d7a5c 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
@@ -48,7 +48,7 @@
   FONT_VARIANT_BODY = 2;
 
   // Placeholder for a custom font variant in the renderer.
-  FONT_VARIANT_EXTENSION_1 = 3;
+  FONT_VARIANT_CUSTOM_1 = 3;
 }
 
 // An extensible FontVariant property.
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/modifiers.proto b/wear/protolayout/protolayout-proto/src/main/proto/modifiers.proto
index 4b4a012..010004ed 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/modifiers.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/modifiers.proto
@@ -183,6 +183,9 @@
   //
   // Defaults to false (i.e. not hidden).
   BoolProp hidden = 8;
+
+  // The optional identifier for the layout element.
+  string id = 9;
 }
 
 // The content transition of an element. Any update to the element or its
diff --git a/wear/watchface/watchface-client-guava/api/current.ignore b/wear/watchface/watchface-client-guava/api/current.ignore
new file mode 100644
index 0000000..91dda93
--- /dev/null
+++ b/wear/watchface/watchface-client-guava/api/current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedThrows: androidx.wear.watchface.client.ListenableWatchFaceControlClient#getOrCreateInteractiveWatchFaceClient(String, androidx.wear.watchface.client.DeviceConfig, androidx.wear.watchface.client.WatchUiState, androidx.wear.watchface.style.UserStyleData, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>, java.util.concurrent.Executor, androidx.core.util.Consumer<java.lang.String>, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient>):
+    Method androidx.wear.watchface.client.ListenableWatchFaceControlClient.getOrCreateInteractiveWatchFaceClient no longer throws exception android.os.RemoteException
+
+
+ParameterNameChange: androidx.wear.watchface.client.ListenableWatchFaceControlClient#getOrCreateInteractiveWatchFaceClient(String, androidx.wear.watchface.client.DeviceConfig, androidx.wear.watchface.client.WatchUiState, androidx.wear.watchface.style.UserStyleData, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>, java.util.concurrent.Executor, androidx.core.util.Consumer<java.lang.String>, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient>) parameter #7:
+    Attempted to remove parameter name from parameter arg8 in androidx.wear.watchface.client.ListenableWatchFaceControlClient.getOrCreateInteractiveWatchFaceClient
diff --git a/wear/watchface/watchface-client-guava/api/restricted_current.ignore b/wear/watchface/watchface-client-guava/api/restricted_current.ignore
new file mode 100644
index 0000000..91dda93
--- /dev/null
+++ b/wear/watchface/watchface-client-guava/api/restricted_current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedThrows: androidx.wear.watchface.client.ListenableWatchFaceControlClient#getOrCreateInteractiveWatchFaceClient(String, androidx.wear.watchface.client.DeviceConfig, androidx.wear.watchface.client.WatchUiState, androidx.wear.watchface.style.UserStyleData, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>, java.util.concurrent.Executor, androidx.core.util.Consumer<java.lang.String>, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient>):
+    Method androidx.wear.watchface.client.ListenableWatchFaceControlClient.getOrCreateInteractiveWatchFaceClient no longer throws exception android.os.RemoteException
+
+
+ParameterNameChange: androidx.wear.watchface.client.ListenableWatchFaceControlClient#getOrCreateInteractiveWatchFaceClient(String, androidx.wear.watchface.client.DeviceConfig, androidx.wear.watchface.client.WatchUiState, androidx.wear.watchface.style.UserStyleData, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>, java.util.concurrent.Executor, androidx.core.util.Consumer<java.lang.String>, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient>) parameter #7:
+    Attempted to remove parameter name from parameter arg8 in androidx.wear.watchface.client.ListenableWatchFaceControlClient.getOrCreateInteractiveWatchFaceClient
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt
index 57efd34..0b32c3e 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt
@@ -57,6 +57,7 @@
 import kotlinx.coroutines.CompletableDeferred
 import org.junit.After
 import org.junit.Assert
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito
@@ -191,6 +192,7 @@
     }
 
     @Test
+    @Ignore("b/281083901")
     public fun listenableOpenComplicationDataSourceChooser() {
         ComplicationDataSourceChooserContract.useTestComplicationHelperActivity = true
         val chosenComplicationDataSourceInfo =
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
index d8d78fc..53dddff 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
@@ -86,6 +86,7 @@
 import org.junit.Assert.fail
 import org.junit.Assume
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -378,6 +379,7 @@
 
     @SuppressLint("NewApi")
     @Test
+    @Ignore("b/274981990")
     public fun testCommandTakeOpenGLScreenShot() {
         val latch = CountDownLatch(1)
 
diff --git a/wear/wear-input-testing/src/main/AndroidManifest.xml b/wear/wear-input-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/wear/wear-input-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/wear/wear-input/src/main/AndroidManifest.xml b/wear/wear-input/src/main/AndroidManifest.xml
deleted file mode 100644
index 9cd4123..0000000
--- a/wear/wear-input/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest />
diff --git a/wear/wear-ongoing/src/main/AndroidManifest.xml b/wear/wear-ongoing/src/main/AndroidManifest.xml
deleted file mode 100644
index 9e46016..0000000
--- a/wear/wear-ongoing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
-
diff --git a/wear/wear/src/androidTest/java/androidx/wear/activity/ConfirmationActivityTest.java b/wear/wear/src/androidTest/java/androidx/wear/activity/ConfirmationActivityTest.java
index 2e2720a..36e4a604 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/activity/ConfirmationActivityTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/activity/ConfirmationActivityTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.widget.Button;
+import org.junit.Ignore;
 
 import androidx.annotation.Nullable;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -56,6 +57,7 @@
             new ActivityTestRule<>(ConfirmationActivityTestActivity.class, true, true);
 
     @Test
+    @Ignore("b/272346886")
     public void testConfirmationDialogShownForDefaultDuration() throws Throwable {
         int testDuration = ConfirmationActivity.DEFAULT_ANIMATION_DURATION_MILLIS;
         // Check that the structure of the test is still valid
@@ -66,12 +68,14 @@
     }
 
     @Test
+    @Ignore("b/272346886")
     public void testConfirmationDialogShownForLongerDuration() throws Throwable {
         testConfirmationDialogShownForConfiguredDuration(
                 ConfirmationActivity.DEFAULT_ANIMATION_DURATION_MILLIS * 2, "A message");
     }
 
     @Test
+    @Ignore("b/272346886")
     public void testConfirmationDialogWithMissingMessage() throws Throwable {
         testConfirmationDialogShownForConfiguredDuration(
                 ConfirmationActivity.DEFAULT_ANIMATION_DURATION_MILLIS * 2, /* message= */null);
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/ArcLayoutTest.kt b/wear/wear/src/androidTest/java/androidx/wear/widget/ArcLayoutTest.kt
index b03ecf3..3409862 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/ArcLayoutTest.kt
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/ArcLayoutTest.kt
@@ -58,6 +58,7 @@
 import org.hamcrest.CoreMatchers.any
 import org.hamcrest.Matcher
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -830,6 +831,7 @@
     }
 
     @Test(timeout = 5000)
+    @Ignore("b/280671279")
     fun testBasicTouch() {
         val context: Context = ApplicationProvider.getApplicationContext()
         // This views are the same as the test testTouchEvents()
@@ -873,6 +875,7 @@
     }
 
     @Test(timeout = 10000)
+    @Ignore("b/280671279")
     fun testMarginTouch() {
         val views = createTwoArcsWithMargin()
         testEventsFast("touch_fast_margin_screenshot", views)
diff --git a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/ProcessGlobalConfigActivityTestAppTest.java b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/ProcessGlobalConfigActivityTestAppTest.java
index 913e8b5..64b80a7 100644
--- a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/ProcessGlobalConfigActivityTestAppTest.java
+++ b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/ProcessGlobalConfigActivityTestAppTest.java
@@ -24,6 +24,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.webkit.WebViewFeature;
+import org.junit.Ignore;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -42,6 +43,7 @@
             new ActivityScenarioRule<>(ProcessGlobalConfigActivity.class);
 
     @Test
+    @Ignore("b/280671406")
     public void testSetDataDirectorySuffix() throws Throwable {
         WebkitTestHelpers.assumeStartupFeature(
                 WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX,
diff --git a/webkit/webkit/src/main/AndroidManifest.xml b/webkit/webkit/src/main/AndroidManifest.xml
deleted file mode 100644
index 95c4426..0000000
--- a/webkit/webkit/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest />
diff --git a/window/extensions/extensions/src/main/AndroidManifest.xml b/window/extensions/extensions/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/window/extensions/extensions/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/window/sidecar/sidecar/src/main/AndroidManifest.xml b/window/sidecar/sidecar/src/main/AndroidManifest.xml
deleted file mode 100644
index deac81d..0000000
--- a/window/sidecar/sidecar/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-  Copyright 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest />
diff --git a/work/work-benchmark/src/main/AndroidManifest.xml b/work/work-benchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index 4d64e1c..0000000
--- a/work/work-benchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest />
diff --git a/work/work-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt b/work/work-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt
index 639d5b1..0660ca2 100644
--- a/work/work-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt
+++ b/work/work-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt
@@ -23,6 +23,7 @@
 import androidx.work.NetworkType
 import androidx.work.OneTimeWorkRequestBuilder
 import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.SystemClock
 import androidx.work.impl.WorkManagerImpl
 import androidx.work.impl.background.gcm.GcmTaskConverter.EXECUTION_WINDOW_SIZE_IN_SECONDS
 import com.google.android.gms.gcm.Task
@@ -45,7 +46,7 @@
 
     @Before
     fun setUp() {
-        mTaskConverter = spy(GcmTaskConverter())
+        mTaskConverter = spy(GcmTaskConverter(SystemClock()))
     }
 
     @Test
diff --git a/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmScheduler.java b/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmScheduler.java
index f8ffaba..71f595e 100644
--- a/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmScheduler.java
+++ b/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmScheduler.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import androidx.annotation.NonNull;
+import androidx.work.Clock;
 import androidx.work.Logger;
 import androidx.work.impl.Scheduler;
 import androidx.work.impl.model.WorkSpec;
@@ -38,14 +39,14 @@
     private final GcmNetworkManager mNetworkManager;
     private final GcmTaskConverter mTaskConverter;
 
-    public GcmScheduler(@NonNull Context context) {
+    public GcmScheduler(@NonNull Context context, @NonNull Clock clock) {
         boolean isPlayServicesAvailable = GoogleApiAvailability.getInstance()
                 .isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
         if (!isPlayServicesAvailable) {
             throw new IllegalStateException("Google Play Services not available");
         }
         mNetworkManager = GcmNetworkManager.getInstance(context);
-        mTaskConverter = new GcmTaskConverter();
+        mTaskConverter = new GcmTaskConverter(clock);
     }
 
     @Override
diff --git a/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmTaskConverter.java b/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmTaskConverter.java
index 6dec44e..fb6e1ae 100644
--- a/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmTaskConverter.java
+++ b/work/work-gcm/src/main/java/androidx/work/impl/background/gcm/GcmTaskConverter.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
+import androidx.work.Clock;
 import androidx.work.Constraints;
 import androidx.work.NetworkType;
 import androidx.work.impl.model.WorkSpec;
@@ -40,7 +41,6 @@
  * Converts a {@link androidx.work.impl.model.WorkSpec} to a {@link Task}.
  */
 public class GcmTaskConverter {
-
     /**
      * This is referring to the size of the execution window in seconds. {@link GcmNetworkManager}
      * requires that we specify a window of time relative to {@code now} where a {@link Task}
@@ -53,6 +53,11 @@
     public static final long EXECUTION_WINDOW_SIZE_IN_SECONDS = 5L;
 
     static final String EXTRA_WORK_GENERATION = "androidx.work.impl.background.gcm.GENERATION";
+    private final Clock mClock;
+
+    public GcmTaskConverter(@NonNull Clock clock) {
+        mClock = clock;
+    }
 
     OneoffTask convert(@NonNull WorkSpec workSpec) {
         Bundle extras = new Bundle();
@@ -81,7 +86,7 @@
      */
     @VisibleForTesting
     public long now() {
-        return System.currentTimeMillis();
+        return mClock.currentTimeMillis();
     }
 
     private static Task.Builder applyConstraints(
diff --git a/work/work-runtime/api/current.txt b/work/work-runtime/api/current.txt
index 2b6df42d..42fc6a2 100644
--- a/work/work-runtime/api/current.txt
+++ b/work/work-runtime/api/current.txt
@@ -336,7 +336,7 @@
   }
 
   public final class WorkInfo {
-    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long earliestPossibleRuntimeMillis);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long nextScheduleTimeMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints);
@@ -346,10 +346,10 @@
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags);
     method public androidx.work.Constraints getConstraints();
-    method public long getEarliestPossibleRuntimeMillis();
     method public int getGeneration();
     method public java.util.UUID getId();
     method public long getInitialDelayMillis();
+    method public long getNextScheduleTimeMillis();
     method public androidx.work.Data getOutputData();
     method public androidx.work.WorkInfo.PeriodicityInfo? getPeriodicityInfo();
     method public androidx.work.Data getProgress();
@@ -357,10 +357,10 @@
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String> getTags();
     property public final androidx.work.Constraints constraints;
-    property public final long earliestPossibleRuntimeMillis;
     property public final int generation;
     property public final java.util.UUID id;
     property public final long initialDelayMillis;
+    property public final long nextScheduleTimeMillis;
     property public final androidx.work.Data outputData;
     property public final androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo;
     property public final androidx.work.Data progress;
diff --git a/work/work-runtime/api/public_plus_experimental_current.txt b/work/work-runtime/api/public_plus_experimental_current.txt
index 2b6df42d..42fc6a2 100644
--- a/work/work-runtime/api/public_plus_experimental_current.txt
+++ b/work/work-runtime/api/public_plus_experimental_current.txt
@@ -336,7 +336,7 @@
   }
 
   public final class WorkInfo {
-    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long earliestPossibleRuntimeMillis);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long nextScheduleTimeMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints);
@@ -346,10 +346,10 @@
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags);
     method public androidx.work.Constraints getConstraints();
-    method public long getEarliestPossibleRuntimeMillis();
     method public int getGeneration();
     method public java.util.UUID getId();
     method public long getInitialDelayMillis();
+    method public long getNextScheduleTimeMillis();
     method public androidx.work.Data getOutputData();
     method public androidx.work.WorkInfo.PeriodicityInfo? getPeriodicityInfo();
     method public androidx.work.Data getProgress();
@@ -357,10 +357,10 @@
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String> getTags();
     property public final androidx.work.Constraints constraints;
-    property public final long earliestPossibleRuntimeMillis;
     property public final int generation;
     property public final java.util.UUID id;
     property public final long initialDelayMillis;
+    property public final long nextScheduleTimeMillis;
     property public final androidx.work.Data outputData;
     property public final androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo;
     property public final androidx.work.Data progress;
diff --git a/work/work-runtime/api/restricted_current.txt b/work/work-runtime/api/restricted_current.txt
index 2b6df42d..42fc6a2 100644
--- a/work/work-runtime/api/restricted_current.txt
+++ b/work/work-runtime/api/restricted_current.txt
@@ -336,7 +336,7 @@
   }
 
   public final class WorkInfo {
-    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long earliestPossibleRuntimeMillis);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long nextScheduleTimeMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints);
@@ -346,10 +346,10 @@
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags);
     method public androidx.work.Constraints getConstraints();
-    method public long getEarliestPossibleRuntimeMillis();
     method public int getGeneration();
     method public java.util.UUID getId();
     method public long getInitialDelayMillis();
+    method public long getNextScheduleTimeMillis();
     method public androidx.work.Data getOutputData();
     method public androidx.work.WorkInfo.PeriodicityInfo? getPeriodicityInfo();
     method public androidx.work.Data getProgress();
@@ -357,10 +357,10 @@
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String> getTags();
     property public final androidx.work.Constraints constraints;
-    property public final long earliestPossibleRuntimeMillis;
     property public final int generation;
     property public final java.util.UUID id;
     property public final long initialDelayMillis;
+    property public final long nextScheduleTimeMillis;
     property public final androidx.work.Data outputData;
     property public final androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo;
     property public final androidx.work.Data progress;
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
index 0528bf1..6845d6c 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
@@ -1308,7 +1308,7 @@
 
     @Test
     @SmallTest
-    public void testGetWorkInfoById_earliestPossibleRunTime_notEnqueued()
+    public void testGetWorkInfoById_nextScheduleTime_notEnqueued()
             throws ExecutionException, InterruptedException {
         OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
         work.getWorkSpec().state = RUNNING;
@@ -1318,12 +1318,12 @@
         WorkInfo info = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
 
         assertThat(info.getState(), equalTo(RUNNING));
-        assertThat(info.getEarliestPossibleRuntimeMillis(), equalTo(Long.MAX_VALUE));
+        assertThat(info.getNextScheduleTimeMillis(), equalTo(Long.MAX_VALUE));
     }
 
     @Test
     @SmallTest
-    public void testGetWorkInfoById_earliestPossibleRunTime_enqueued()
+    public void testGetWorkInfoById_nextScheduleTime_enqueued()
             throws ExecutionException, InterruptedException {
         OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
         work.getWorkSpec().lastEnqueueTime = 1000L;
@@ -1332,14 +1332,14 @@
         WorkInfo info = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
 
         assertThat(info.getState(), equalTo(ENQUEUED));
-        assertThat(info.getEarliestPossibleRuntimeMillis(),
+        assertThat(info.getNextScheduleTimeMillis(),
                 equalTo(1000L));
     }
 
     @Test
     @SmallTest
     @SdkSuppress(minSdkVersion = 26)
-    public void testGetWorkInfoById_earliestPossibleRunTime_onetime_initialDelay()
+    public void testGetWorkInfoById_nextScheduleTime_onetime_initialDelay()
             throws ExecutionException, InterruptedException {
         OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).setInitialDelay(
                 Duration.ofMillis(2000)).build();
@@ -1349,14 +1349,14 @@
         WorkInfo info = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
 
         assertThat(info.getState(), equalTo(ENQUEUED));
-        assertThat(info.getEarliestPossibleRuntimeMillis(),
+        assertThat(info.getNextScheduleTimeMillis(),
                 equalTo(3000L));
     }
 
     @Test
     @SmallTest
     @SdkSuppress(minSdkVersion = 26)
-    public void testGetWorkInfoById_earliestPossibleRunTime_periodic_period()
+    public void testGetWorkInfoById_nextScheduleTime_periodic_period()
             throws ExecutionException, InterruptedException {
         Duration period = Duration.ofMinutes(15);
         Duration initialDelay = Duration.ofMillis(2000);
@@ -1374,7 +1374,7 @@
         WorkInfo info = mWorkManagerImpl.getWorkInfoById(work0.getId()).get();
 
         assertThat(info.getState(), equalTo(ENQUEUED));
-        assertThat(info.getEarliestPossibleRuntimeMillis(),
+        assertThat(info.getNextScheduleTimeMillis(),
                 equalTo(lastEnqueueTime.plus(period).toMillis()));
         assertThat(info.getInitialDelayMillis(), equalTo(initialDelay.toMillis()));
     }
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt
index ffce534..5cba6e9 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/greedy/DelayedWorkTrackerTest.kt
@@ -20,6 +20,7 @@
 import androidx.test.filters.MediumTest
 import androidx.work.OneTimeWorkRequest
 import androidx.work.RunnableScheduler
+import androidx.work.SystemClock
 import androidx.work.worker.TestWorker
 import org.junit.Before
 import org.junit.Test
@@ -40,7 +41,7 @@
     fun setUp() {
         mScheduler = mock(GreedyScheduler::class.java)
         mRunnableScheduler = mock(RunnableScheduler::class.java)
-        mDelayedWorkTracker = DelayedWorkTracker(mScheduler, mRunnableScheduler)
+        mDelayedWorkTracker = DelayedWorkTracker(mScheduler, mRunnableScheduler, SystemClock())
     }
 
     @Test
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java
index 2b97041..d000c32 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java
@@ -44,6 +44,7 @@
 import androidx.work.NetworkType;
 import androidx.work.OneTimeWorkRequest;
 import androidx.work.PeriodicWorkRequest;
+import androidx.work.SystemClock;
 import androidx.work.WorkManagerTest;
 import androidx.work.impl.WorkManagerImpl;
 import androidx.work.impl.model.WorkSpec;
@@ -70,7 +71,7 @@
     @Before
     public void setUp() {
         mConverter = new SystemJobInfoConverter(
-                ApplicationProvider.getApplicationContext());
+                ApplicationProvider.getApplicationContext(), new SystemClock());
     }
 
     @Test
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobSchedulerTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobSchedulerTest.java
index 305ccf2..164b411 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobSchedulerTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobSchedulerTest.java
@@ -126,7 +126,7 @@
                         workDatabase,
                         configuration,
                         mJobScheduler,
-                        new SystemJobInfoConverter(context)));
+                        new SystemJobInfoConverter(context, configuration.getClock())));
 
         doNothing().when(mSystemJobScheduler).scheduleInternal(any(WorkSpec.class), anyInt());
     }
diff --git a/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt b/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt
index 67c79d0..943e845 100644
--- a/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt
+++ b/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt
@@ -104,7 +104,7 @@
      * Even if this value is set, the work may not be registered with the system scheduler if
      * there are limited scheduling slots or other factors.
      */
-    val earliestPossibleRuntimeMillis: Long = Long.MAX_VALUE,
+    val nextScheduleTimeMillis: Long = Long.MAX_VALUE,
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -118,7 +118,7 @@
         if (constraints != workInfo.constraints) return false
         if (initialDelayMillis != workInfo.initialDelayMillis) return false
         if (periodicityInfo != workInfo.periodicityInfo) return false
-        if (earliestPossibleRuntimeMillis != workInfo.earliestPossibleRuntimeMillis) return false
+        if (nextScheduleTimeMillis != workInfo.nextScheduleTimeMillis) return false
         return if (tags != workInfo.tags) false else progress == workInfo.progress
     }
 
@@ -133,7 +133,7 @@
         result = 31 * result + constraints.hashCode()
         result = 31 * result + initialDelayMillis.hashCode()
         result = 31 * result + periodicityInfo.hashCode()
-        result = 31 * result + earliestPossibleRuntimeMillis.hashCode()
+        result = 31 * result + nextScheduleTimeMillis.hashCode()
         return result
     }
 
@@ -143,7 +143,7 @@
             "runAttemptCount=$runAttemptCount, generation=$generation, " +
             "constraints=$constraints}, initialDelayMillis=$initialDelayMillis, " +
             "periodicityInfo=$periodicityInfo, " +
-            "earliestPossibleRunTimeMillis=$earliestPossibleRuntimeMillis")
+            "nextScheduleTimeMillis=$nextScheduleTimeMillis")
     }
 
     /**
diff --git a/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt b/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt
index 60dd8fe..27d7f35 100644
--- a/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt
+++ b/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt
@@ -224,7 +224,7 @@
          * Sets an initial delay for the [WorkRequest].
          *
          * @param duration The length of the delay
-         * @return The current [Builder]         *
+         * @return The current [Builder]
          * @throws IllegalArgumentException if the given initial delay will push the execution time
          * past `Long.MAX_VALUE` and cause an overflow
          */
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/Schedulers.java b/work/work-runtime/src/main/java/androidx/work/impl/Schedulers.java
index 6a1ed29..b0786fe 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/Schedulers.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/Schedulers.java
@@ -26,6 +26,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.work.Clock;
 import androidx.work.Configuration;
 import androidx.work.Logger;
 import androidx.work.impl.background.systemalarm.SystemAlarmScheduler;
@@ -100,13 +101,13 @@
             List<WorkSpec> contentUriWorkSpecs = null;
             if (Build.VERSION.SDK_INT >= CONTENT_URI_TRIGGER_API_LEVEL) {
                 contentUriWorkSpecs = workSpecDao.getEligibleWorkForSchedulingWithContentUris();
-                markScheduled(workSpecDao, contentUriWorkSpecs);
+                markScheduled(workSpecDao, configuration.getClock(), contentUriWorkSpecs);
             }
 
             // Enqueued workSpecs when scheduling limits are applicable.
             eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
                     configuration.getMaxSchedulerLimit());
-            markScheduled(workSpecDao, eligibleWorkSpecsForLimitedSlots);
+            markScheduled(workSpecDao, configuration.getClock(), eligibleWorkSpecsForLimitedSlots);
             if (contentUriWorkSpecs != null) {
                 eligibleWorkSpecsForLimitedSlots.addAll(contentUriWorkSpecs);
             }
@@ -157,7 +158,7 @@
             setComponentEnabled(context, SystemJobService.class, true);
             Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
         } else {
-            scheduler = tryCreateGcmBasedScheduler(context);
+            scheduler = tryCreateGcmBasedScheduler(context, configuration.getClock());
             if (scheduler == null) {
                 scheduler = new SystemAlarmScheduler(context);
                 setComponentEnabled(context, SystemAlarmService.class, true);
@@ -168,11 +169,12 @@
     }
 
     @Nullable
-    private static Scheduler tryCreateGcmBasedScheduler(@NonNull Context context) {
+    private static Scheduler tryCreateGcmBasedScheduler(@NonNull Context context, Clock clock) {
         try {
             Class<?> klass = Class.forName(GCM_SCHEDULER);
             Scheduler scheduler =
-                    (Scheduler) klass.getConstructor(Context.class).newInstance(context);
+                    (Scheduler) klass.getConstructor(Context.class, Clock.class)
+                            .newInstance(context, clock);
             Logger.get().debug(TAG, "Created " + GCM_SCHEDULER);
             return scheduler;
         } catch (Throwable throwable) {
@@ -184,9 +186,9 @@
     private Schedulers() {
     }
 
-    private static void markScheduled(WorkSpecDao dao, List<WorkSpec> workSpecs) {
+    private static void markScheduled(WorkSpecDao dao, Clock clock, List<WorkSpec> workSpecs) {
         if (workSpecs.size() > 0) {
-            long now = System.currentTimeMillis();
+            long now = clock.currentTimeMillis();
 
             // Mark all the WorkSpecs as scheduled.
             // Calls to Scheduler#schedule() could potentially result in more schedules
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java
index 01e484a..3ae8711 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/DelayedWorkTracker.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.work.Clock;
 import androidx.work.Logger;
 import androidx.work.RunnableScheduler;
 import androidx.work.impl.model.WorkSpec;
@@ -43,14 +44,17 @@
     final GreedyScheduler mGreedyScheduler;
 
     private final RunnableScheduler mRunnableScheduler;
+    private final Clock mClock;
     private final Map<String, Runnable> mRunnables;
 
     public DelayedWorkTracker(
             @NonNull GreedyScheduler scheduler,
-            @NonNull RunnableScheduler runnableScheduler) {
+            @NonNull RunnableScheduler runnableScheduler,
+            @NonNull Clock clock) {
 
         mGreedyScheduler = scheduler;
         mRunnableScheduler = runnableScheduler;
+        mClock = clock;
         mRunnables = new HashMap<>();
     }
 
@@ -77,7 +81,7 @@
         };
 
         mRunnables.put(workSpec.id, runnable);
-        long now = System.currentTimeMillis();
+        long now = mClock.currentTimeMillis();
         long delay = nextRunTime - now;
         mRunnableScheduler.scheduleWithDelay(delay, runnable);
     }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
index b7e8767..bb48033 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
@@ -93,7 +93,8 @@
     ) {
         mContext = context;
         mWorkConstraintsTracker = new WorkConstraintsTrackerImpl(trackers, this);
-        mDelayedWorkTracker = new DelayedWorkTracker(this, configuration.getRunnableScheduler());
+        mDelayedWorkTracker = new DelayedWorkTracker(
+                this, configuration.getRunnableScheduler(), configuration.getClock());
         mConfiguration = configuration;
         mProcessor = processor;
         mWorkLauncher = workLauncher;
@@ -152,7 +153,7 @@
             }
             long throttled = throttleIfNeeded(workSpec);
             long nextRunTime = max(workSpec.calculateNextRunTime(), throttled);
-            long now = System.currentTimeMillis();
+            long now = mConfiguration.getClock().currentTimeMillis();
             if (workSpec.state == WorkInfo.State.ENQUEUED) {
                 if (now < nextRunTime) {
                     // Future work
@@ -290,7 +291,7 @@
             AttemptData firstRunAttempt = mFirstRunAttempts.get(id);
             if (firstRunAttempt == null) {
                 firstRunAttempt = new AttemptData(workSpec.runAttemptCount,
-                        System.currentTimeMillis());
+                        mConfiguration.getClock().currentTimeMillis());
                 mFirstRunAttempts.put(id, firstRunAttempt);
             }
             return firstRunAttempt.mTimeStamp
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java b/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java
index ade9938..1e73418 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java
@@ -24,6 +24,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.WorkerThread;
+import androidx.work.Clock;
 import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.StartStopToken;
@@ -128,10 +129,13 @@
     private final Context mContext;
     private final Map<WorkGenerationalId, DelayMetCommandHandler> mPendingDelayMet;
     private final Object mLock;
+    private final Clock mClock;
     private final StartStopTokens mStartStopTokens;
 
-    CommandHandler(@NonNull Context context, @NonNull StartStopTokens startStopTokens) {
+    CommandHandler(@NonNull Context context, Clock clock,
+            @NonNull StartStopTokens startStopTokens) {
         mContext = context;
+        mClock = clock;
         mStartStopTokens = startStopTokens;
         mPendingDelayMet = new HashMap<>();
         mLock = new Object();
@@ -332,7 +336,7 @@
         // Constraints changed command handler is synchronous. No cleanup
         // is necessary.
         ConstraintsCommandHandler changedCommandHandler =
-                new ConstraintsCommandHandler(mContext, startId, dispatcher);
+                new ConstraintsCommandHandler(mContext, mClock, startId, dispatcher);
         changedCommandHandler.handleConstraintsChanged();
     }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java b/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java
index ad3361b..4bd3fbf 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java
@@ -24,6 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.WorkerThread;
+import androidx.work.Clock;
 import androidx.work.Logger;
 import androidx.work.impl.constraints.WorkConstraintsTrackerImpl;
 import androidx.work.impl.constraints.trackers.Trackers;
@@ -35,7 +36,6 @@
 /**
  * This is a command handler which handles the constraints changed event.
  * Typically this happens for WorkSpec's for which we have pending alarms.
- *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class ConstraintsCommandHandler {
@@ -43,16 +43,18 @@
     private static final String TAG = Logger.tagWithPrefix("ConstraintsCmdHandler");
 
     private final Context mContext;
+    private final Clock mClock;
     private final int mStartId;
     private final SystemAlarmDispatcher mDispatcher;
     private final WorkConstraintsTrackerImpl mWorkConstraintsTracker;
 
     ConstraintsCommandHandler(
             @NonNull Context context,
+            Clock clock,
             int startId,
             @NonNull SystemAlarmDispatcher dispatcher) {
-
         mContext = context;
+        mClock = clock;
         mStartId = startId;
         mDispatcher = dispatcher;
         Trackers trackers = mDispatcher.getWorkManager().getTrackers();
@@ -74,7 +76,7 @@
 
         List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
         // Filter candidates should have already been scheduled.
-        long now = System.currentTimeMillis();
+        long now = mClock.currentTimeMillis();
         for (WorkSpec workSpec : candidates) {
             String workSpecId = workSpec.id;
             long triggerAt = workSpec.calculateNextRunTime();
@@ -87,7 +89,8 @@
         for (WorkSpec workSpec : eligibleWorkSpecs) {
             String workSpecId = workSpec.id;
             Intent intent = CommandHandler.createDelayMetIntent(mContext, generationalId(workSpec));
-            Logger.get().debug(TAG, "Creating a delay_met command for workSpec with id (" + workSpecId + ")");
+            Logger.get().debug(TAG,
+                    "Creating a delay_met command for workSpec with id (" + workSpecId + ")");
             mDispatcher.getTaskExecutor().getMainThreadExecutor().execute(
                     new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
         }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java b/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
index c92310b..d6a5ef4 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
@@ -90,8 +90,9 @@
     ) {
         mContext = context.getApplicationContext();
         mStartStopTokens = new StartStopTokens();
-        mCommandHandler = new CommandHandler(mContext, mStartStopTokens);
         mWorkManager = workManager != null ? workManager : WorkManagerImpl.getInstance(context);
+        mCommandHandler = new CommandHandler(
+                mContext, mWorkManager.getConfiguration().getClock(), mStartStopTokens);
         mWorkTimer = new WorkTimer(mWorkManager.getConfiguration().getRunnableScheduler());
         mProcessor = processor != null ? processor : mWorkManager.getProcessor();
         mTaskExecutor = mWorkManager.getWorkTaskExecutor();
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java b/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
index 61a24d5..f292f3f 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
@@ -29,6 +29,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.work.BackoffPolicy;
+import androidx.work.Clock;
 import androidx.work.Constraints;
 import androidx.work.Logger;
 import androidx.work.NetworkType;
@@ -50,8 +51,10 @@
     static final String EXTRA_WORK_SPEC_GENERATION = "EXTRA_WORK_SPEC_GENERATION";
 
     private final ComponentName mWorkServiceComponent;
+    private final Clock mClock;
 
-    SystemJobInfoConverter(@NonNull Context context) {
+    SystemJobInfoConverter(@NonNull Context context, Clock clock) {
+        mClock = clock;
         Context appContext = context.getApplicationContext();
         mWorkServiceComponent = new ComponentName(appContext, SystemJobService.class);
     }
@@ -86,7 +89,7 @@
         }
 
         long nextRunTime = workSpec.calculateNextRunTime();
-        long now = System.currentTimeMillis();
+        long now = mClock.currentTimeMillis();
         long offset = Math.max(nextRunTime - now, 0);
 
         if (Build.VERSION.SDK_INT <= 28) {
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java b/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
index af76cf1..fc679669 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
@@ -78,7 +78,7 @@
                 workDatabase,
                 configuration,
                 (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),
-                new SystemJobInfoConverter(context)
+                new SystemJobInfoConverter(context, configuration.getClock())
         );
     }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java b/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java
index 03158ae..71e3ec8 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java
@@ -222,7 +222,9 @@
                     }
                     // Update the last cancelled time in Preference.
                     new PreferenceUtils(workManagerImpl.getWorkDatabase())
-                            .setLastCancelAllTimeMillis(System.currentTimeMillis());
+                            .setLastCancelAllTimeMillis(
+                                    workManagerImpl.getConfiguration().getClock()
+                                            .currentTimeMillis());
                     workDatabase.setTransactionSuccessful();
                 } finally {
                     workDatabase.endTransaction();
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt b/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt
index 2781cdf..24221a9 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt
@@ -38,7 +38,8 @@
         val workNameDao = database.workNameDao()
         val workTagDao = database.workTagDao()
         val systemIdInfoDao = database.systemIdInfoDao()
-        val startAt = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)
+        val startAt =
+            workManager.configuration.clock.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)
         val completed = workSpecDao.getRecentlyCompletedWork(startAt)
         val running = workSpecDao.getRunningWork()
         val enqueued = workSpecDao.getAllEligibleWorkSpecsForScheduling(
diff --git a/work/work-testing/src/androidTest/java/androidx/work/testing/CustomClockTest.kt b/work/work-testing/src/androidTest/java/androidx/work/testing/CustomClockTest.kt
index 101c334..002e439 100644
--- a/work/work-testing/src/androidTest/java/androidx/work/testing/CustomClockTest.kt
+++ b/work/work-testing/src/androidTest/java/androidx/work/testing/CustomClockTest.kt
@@ -60,7 +60,7 @@
     @Test
     @SdkSuppress(minSdkVersion = 26)
     @Throws(InterruptedException::class, ExecutionException::class)
-    fun testScheduledTime_relativeToTestClock() {
+    fun testScheduleTime_relativeToTestClock() {
         testClock.timeMillis = Duration.ofDays(100).toMillis()
         val initialDelay = Duration.ofHours(1)
 
@@ -72,7 +72,7 @@
         workManagerImpl.enqueue(listOf(request)).result.get()
 
         val status = workManagerImpl.getWorkInfoById(request.id).get()
-        assertThat(status.earliestPossibleRuntimeMillis)
+        assertThat(status.nextScheduleTimeMillis)
             .isEqualTo(testClock.timeMillis + initialDelay.toMillis())
     }
 }